OCaml Library Collection
/

Cookie management library for OCaml

HTTP cookies are a mechanism defined in RFC 6265 that allows "server side connections to store and retrieve information on the client side." Originally designed to enable persistent client-side state for web applications, cookies are essential for storing user preferences, session data, shopping cart contents, and authentication tokens.

This library provides a complete cookie implementation following RFC 6265 while integrating Eio for efficient asynchronous operations.

Cookies are set via the Set-Cookie HTTP response header (Section 4.1) with the basic format: NAME=VALUE with optional attributes including:

  • expires: Cookie lifetime specification (Section 5.2.1)
  • max-age: Cookie lifetime in seconds (Section 5.2.2)
  • domain: Valid domains using tail matching (Section 5.2.3)
  • path: URL subset for cookie validity (Section 5.2.4)
  • secure: Transmission over secure channels only (Section 5.2.5)
  • httponly: Not accessible to JavaScript (Section 5.2.6)
  • samesite: Cross-site request behavior (RFC 6265bis)
  • partitioned: CHIPS partitioned storage

Domain and Path Matching

The library implements standard domain and path matching rules from Section 5.1.3 and Section 5.1.4:

  • Domain matching uses suffix matching for hostnames (e.g., "example.com" matches "sub.example.com")
  • IP addresses require exact match only
  • Path matching requires exact match or prefix with "/" separator

Standards and References

This library implements and references the following IETF specifications:

Additional standards:

  • Publicsuffix - Public Suffix List lookup used for domain validation
  • Cookeio_jar - Cookie jar storage with persistence support

Types

module SameSite : sig ... end
module Expiration : sig ... end
type t

HTTP Cookie representation with all standard attributes.

A cookie represents a name-value pair with associated metadata that controls its scope, security, and lifetime. Per RFC 6265 Section 5.3, cookies with the same name, domain, and path will overwrite each other when stored.

val domain : t -> string

Get the domain of a cookie.

The domain is normalized per RFC 6265 Section 5.2.3 (leading dots removed).

val path : t -> string

Get the path of a cookie.

val name : t -> string

Get the name of a cookie.

val value : t -> string

Get the value of a cookie.

val value_trimmed : t -> string

Get cookie value with surrounding double-quotes removed if they form a matching pair.

Only removes quotes when both opening and closing quotes are present. The raw value is always preserved in value. This is useful for handling quoted cookie values.

Examples:

  • "value""value"
  • "\"value\"""value"
  • "\"value""\"value" (no matching pair)
  • "\"val\"\"""val\"" (removes outer pair only)
val secure : t -> bool

Check if cookie has the Secure flag.

Per RFC 6265 Section 5.2.5, Secure cookies are only sent over HTTPS connections.

val http_only : t -> bool

Check if cookie has the HttpOnly flag.

Per RFC 6265 Section 5.2.6, HttpOnly cookies are not accessible to client-side scripts.

val partitioned : t -> bool

Check if cookie has the Partitioned attribute.

Partitioned cookies are part of CHIPS (Cookies Having Independent Partitioned State) and are stored separately per top-level site, enabling privacy-preserving third-party cookie functionality. Partitioned cookies must always be Secure.

val host_only : t -> bool

Check if cookie has the host-only flag set.

Per RFC 6265 Section 5.3 Step 6:

  • If the Set-Cookie header included a Domain attribute, host-only-flag is false and the cookie matches the domain and all subdomains.
  • If no Domain attribute was present, host-only-flag is true and the cookie only matches the exact request host.

Example:

  • Cookie set on "example.com" with Domain=example.com: host_only=false, matches example.com and sub.example.com
  • Cookie set on "example.com" without Domain attribute: host_only=true, matches only example.com, not sub.example.com
val expires : t -> Expiration.t option

Get the expiration attribute if set.

Per RFC 6265 Section 5.2.1:

  • None: No expiration specified (session cookie)
  • Some `Session: Session cookie (expires when user agent session ends)
  • Some (`DateTime t): Expires at specific time t

Both max_age and expires can be present simultaneously. This library stores both independently.

val max_age : t -> Ptime.Span.t option

Get the max-age attribute if set.

Per RFC 6265 Section 5.2.2, Max-Age specifies the cookie lifetime in seconds. Both max_age and expires can be present simultaneously. When both are present in a Set-Cookie header, browsers prioritize max_age per Section 5.3 Step 3.

This library stores both independently and serializes both when present.

val same_site : t -> SameSite.t option

Get the same-site policy of a cookie.

val creation_time : t -> Ptime.t

Get the creation time of a cookie.

Per RFC 6265 Section 5.3, this is set when the cookie is first received.

val last_access : t -> Ptime.t

Get the last access time of a cookie.

Per RFC 6265 Section 5.3, this is updated each time the cookie is retrieved for a request.

val make : domain:string -> path:string -> name:string -> value:string -> ?secure:bool -> ?http_only:bool -> ?expires:Expiration.t -> ?max_age:Ptime.Span.t -> ?same_site:SameSite.t -> ?partitioned:bool -> ?host_only:bool -> creation_time:Ptime.t -> last_access:Ptime.t -> unit -> t

Create a new cookie with the given attributes.

  • parameter domain

    The cookie domain (will be normalized)

  • parameter path

    The cookie path

  • parameter name

    The cookie name

  • parameter value

    The cookie value

  • parameter secure

    If true, cookie only sent over HTTPS (default: false)

  • parameter http_only

    If true, cookie not accessible to scripts (default: false)

  • parameter expires

    Expiration time

  • parameter max_age

    Lifetime in seconds

  • parameter same_site

    Cross-site request policy

  • parameter partitioned

    CHIPS partitioned storage (default: false)

  • parameter host_only

    If true, exact domain match only (default: false). Per RFC 6265 Section 5.3, this should be true when no Domain attribute was present in the Set-Cookie header.

  • parameter creation_time

    When the cookie was created

  • parameter last_access

    Last time the cookie was accessed

Note: If partitioned is true, the cookie must also be secure. Invalid combinations will result in validation errors.

RFC 6265 Validation

Validation functions for cookie names, values, and attributes per RFC 6265 Section 4.1.1.

These functions implement the syntactic requirements from RFC 6265 to ensure cookies conform to the specification before being sent in HTTP headers. All validation failures return detailed error messages citing the specific RFC requirement that was violated.

Validation Philosophy

Per RFC 6265 Section 4, there is an important distinction between:

  • Server requirements (Section 4.1): Strict syntax for generating Set-Cookie headers
  • User agent requirements (Section 5): Lenient parsing for receiving Set-Cookie headers

These validation functions enforce the server requirements, ensuring that cookies generated by this library conform to RFC 6265 syntax. When parsing cookies from HTTP headers, the library may be more lenient to maximize interoperability with non-compliant servers.

Character Set Requirements

RFC 6265 restricts cookies to US-ASCII characters with specific exclusions:

  • Cookie names: RFC 2616 tokens (no CTLs, no separators)
  • Cookie values: cookie-octet characters (0x21, 0x23-0x2B, 0x2D-0x3A, 0x3C-0x5B, 0x5D-0x7E)
  • Domain values: RFC 1034 domain name syntax or IP addresses
  • Path values: Any character except CTLs and semicolon

These functions return Ok value on success or Error msg with a detailed explanation of why validation failed.

module Validate : sig ... end

Parse Set-Cookie response header value into a cookie.

Parses a Set-Cookie header following RFC 6265 Section 5.2:

  • Basic format: NAME=VALUE; attribute1; attribute2=value2
  • Supports all standard attributes: expires, max-age, domain, path, secure, httponly, samesite, partitioned
  • Returns Error msg if parsing fails or cookie validation fails, with a detailed explanation of what was invalid
  • The domain and path parameters provide the request context for default values
  • The now parameter is used for calculating expiry times from max-age attributes and setting creation/access times

Validation rules applied:

  • Cookie name must be a valid RFC 2616 token (no CTLs or separators)
  • Cookie value must contain only valid cookie-octets
  • Domain must be a valid domain name (RFC 1034) or IP address
  • Path must not contain control characters or semicolons
  • Max-Age must be non-negative
  • SameSite=None requires the Secure flag to be set (RFC 6265bis)
  • Partitioned requires the Secure flag to be set (CHIPS)
  • Domain must not be a public suffix per RFC 6265 Section 5.3 Step 5 (unless the request host exactly matches the domain). This uses the Mozilla Public Suffix List to prevent domain-wide cookie attacks.

Public Suffix Validation

Cookies with Domain attributes that are public suffixes (e.g., .com, .co.uk, .github.io) are rejected to prevent a malicious site from setting cookies that would affect all sites under that TLD.

Examples:

  • Request from www.example.com, Domain=.com → rejected (public suffix)
  • Request from www.example.com, Domain=.example.com → allowed
  • Request from blogspot.com, Domain=.blogspot.com → allowed (request matches)

Example:

of_set_cookie_header ~now:(fun () -> Ptime_clock.now ())
 ~domain:"example.com" ~path:"/" "session=abc123; Secure; HttpOnly"

Parse Cookie request header containing semicolon-separated name=value pairs.

Parses a Cookie header following RFC 6265 Section 4.2. Cookie headers contain only name=value pairs without attributes: "name1=value1; name2=value2; name3=value3"

Validates each cookie name and value per RFC 6265 and detects duplicate cookie names (which is forbidden per Section 4.2.1).

Creates cookies with:

  • Provided domain and path from request context
  • All security flags set to false (defaults)
  • All optional attributes set to None
  • host_only = true (since we cannot determine from the header alone whether cookies originally had a Domain attribute)
  • creation_time and last_access set to current time from now

Returns Ok cookies if all cookies parse successfully with no duplicates, or Error msg if any validation fails.

Example:

of_cookie_header ~now:(fun () -> Ptime_clock.now ()) ~domain:"example.com"
 ~path:"/" "session=abc; theme=dark"

Create Cookie header value from cookies.

Formats a list of cookies into a Cookie header value suitable for HTTP requests per RFC 6265 Section 4.2.

  • Format: name1=value1; name2=value2; name3=value3
  • Only includes cookie names and values, not attributes
  • Cookies should already be filtered for the target domain/path

Example: make_cookie_header cookies might return "session=abc123; theme=dark"

Create Set-Cookie header value from a cookie.

Formats a cookie into a Set-Cookie header value suitable for HTTP responses per RFC 6265 Section 4.1. Includes all cookie attributes: Max-Age, Expires, Domain, Path, Secure, HttpOnly, Partitioned, and SameSite.

The Expires attribute uses rfc1123-date format ("Sun, 06 Nov 1994 08:49:37 GMT") as specified in Section 4.1.1.

Pretty Printing

val pp : Format.formatter -> t -> unit

Pretty print a cookie.