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.
Cookie Format and Structure
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:
- RFC 6265 - HTTP State Management Mechanism (April 2011) - Primary specification
- RFC 6265bis - Cookies: HTTP State Management Mechanism (Draft) - SameSite attribute and modern updates
- RFC 1034 Section 3.5 - Domain Names - Preferred Name Syntax for domain validation
- RFC 2616 Section 2.2 - HTTP/1.1 - Token syntax definition
- RFC 1123 Section 5.2.14 - Internet Host Requirements - Date format (rfc1123-date)
Additional standards:
- Mozilla Public Suffix List - Registry of public suffixes for cookie domain validation per RFC 6265 Section 5.3 Step 5
Related Libraries
Publicsuffix- Public Suffix List lookup used for domain validationCookeio_jar- Cookie jar storage with persistence support
Types
module SameSite : sig ... endmodule Expiration : sig ... endHTTP 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.
Cookie Accessors
val domain : t -> stringGet the domain of a cookie.
The domain is normalized per RFC 6265 Section 5.2.3 (leading dots removed).
val path : t -> stringGet the path of a cookie.
val name : t -> stringGet the name of a cookie.
val value : t -> stringGet the value of a cookie.
val value_trimmed : t -> stringGet 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 -> boolCheck 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 -> boolCheck 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 -> boolCheck 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 -> boolCheck 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 optionGet the expiration attribute if set.
None: No expiration specified (session cookie)Some `Session: Session cookie (expires when user agent session ends)Some (`DateTime t): Expires at specific timet
Both max_age and expires can be present simultaneously. This library stores both independently.
val max_age : t -> Ptime.Span.t optionGet 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 optionGet the same-site policy of a cookie.
Get the creation time of a cookie.
Per RFC 6265 Section 5.3, this is set when the cookie is first received.
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 ->
tCreate a new cookie with the given attributes.
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 ... endCookie Creation and Parsing
val of_set_cookie_header :
now:(unit -> Ptime.t) ->
domain:string ->
path:string ->
string ->
(t, string) resultParse 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 msgif parsing fails or cookie validation fails, with a detailed explanation of what was invalid - The
domainandpathparameters provide the request context for default values - The
nowparameter is used for calculating expiry times frommax-ageattributes 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=Nonerequires theSecureflag to be set (RFC 6265bis)Partitionedrequires theSecureflag 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"val of_cookie_header :
now:(unit -> Ptime.t) ->
domain:string ->
path:string ->
string ->
(t list, string) resultParse 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
domainandpathfrom 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_timeandlast_accessset to current time fromnow
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"val make_cookie_header : t list -> stringCreate 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"
val make_set_cookie_header : t -> stringCreate 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 -> unitPretty print a cookie.