RFC 2109

HTTP State Management Mechanism

๐Ÿ“… published February 1997, from ietf

RFC 2109: HTTP State Management Mechanism ๐Ÿช

okay bestie, this one is the spec that taught HTTP to remember things ๐Ÿงš๐Ÿ’–. The title block says it's from the Network Working Group, Request for Comments 2109, and it's on the Standards Track ๐ŸŽ€. The authors are D. Kristol (Bell Laboratories, Lucent Technologies) and L. Montulli (Netscape Communications), and it's dated February 1997 ๐ŸŒธ. The title block doesn't list anything it obsoletes or updates, so it's solo-standing ๐Ÿ’….


Status of This Memo

This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited. ๐Ÿ“œโœจ

1. ABSTRACT ๐Ÿฆ‹

This doc is about making a stateful session on top of HTTP requests and responses ๐Ÿ’…. It introduces two new headers, Cookie and Set-Cookie, which carry state info back and forth between origin servers and user agents ๐Ÿช. Netscape already had a cookie proposal, and this method is different from it, but it can still play nicely with HTTP/1.0 user agents that use Netscape's way ๐Ÿซถ. (Check the HISTORICAL section for the tea โ˜•.)

2. TERMINOLOGY ๐Ÿ“–

The terms user agent, client, server, proxy, and origin server mean the same thing as they do in the HTTP/1.0 specification, so no new drama there ๐Ÿ’–.

Fully-qualified host name (FQHN) means either the fully-qualified domain name (FQDN) of a host (a completely specified domain name ending in a top-level domain such as .com or .uk), or the numeric Internet Protocol (IP) address of a host ๐ŸŒˆ. The FQDN is the preferred one, and using numeric IP addresses is strongly discouraged ๐Ÿ™ˆ.

The terms request-host and request-URI refer to the values the client would send to the server as, respectively, the host (but not port) and abs_path portions of the absoluteURI (http_URL) of the HTTP request line ๐ŸŽ€. Heads up: request-host must be a FQHN ๐Ÿ‘‘.

Host names can be given either as an IP address or a FQHN string ๐Ÿ’œ. Sometimes we need to compare one host name with another, and here's the check. Host A's name domain-matches host B's if:

  • both host names are IP addresses and their host name strings match exactly; or
  • both host names are FQDN strings and their host name strings match exactly; or
  • A is a FQDN string and has the form NB, where N is a non-empty name string, B has the form .B', and B' is a FQDN string. (So, x.y.com domain-matches .y.com but not y.com.)

Domain-matching is not commutative ๐Ÿ’€. a.b.c.com domain-matches .c.com, but not the reverse ๐Ÿ”ฅ.

Because Netscape's original implementation used the term, this doc says cookie for the state info that passes between an origin server and a user agent and gets stored by the user agent ๐Ÿ“.

3. STATE AND SESSIONS ๐Ÿง

This section is all about making stateful sessions with HTTP requests and responses โœจ. Right now HTTP servers answer each client request without connecting it to any previous or later request ๐Ÿ˜ญ. This technique lets clients and servers that want to swap state info wrap their HTTP requests and responses in a bigger context, which the doc calls a "session" ๐Ÿ’…. A session could be a "shopping cart" where selections get collected before checkout ๐Ÿ›’, or a magazine browsing system where your previous reading changes what gets shown to you ๐Ÿ“š.

There are, of course, lots of possible contexts and therefore lots of possible kinds of session ๐ŸŒˆ. The designers' idea of sessions created by exchanging cookies has these key attributes:

  1. Each session has a beginning and an end.
  2. Each session is relatively short-lived.
  3. Either the user agent or the origin server may terminate a session.
  4. The session is implicit in the exchange of state information.

4. OUTLINE ๐Ÿ—บ๏ธ

Here's the plan: an origin server sends state info to the user agent, and the user agent sends that state info back to the origin server ๐Ÿ”. The big goal is minimal impact on HTTP and on user agents ๐Ÿ’–. Only origin servers that actually need to keep sessions would feel a real impact, and that impact can mostly stay inside Common Gateway Interface (CGI) programs, unless the server has fancier state management support ๐Ÿงš. (See Implementation Considerations, below. ๐Ÿ‘€)

4.1. Syntax: General ๐Ÿงฉ

The two state management headers, Set-Cookie and Cookie, share some syntax stuff involving attribute-value pairs ๐Ÿ’œ. The grammar below borrows the notation, and the tokens DIGIT (decimal digits) and token (informally, a sequence of non-special, non-white space characters), from the HTTP/1.1 specification [RFC 2068] ๐Ÿ“–.

av-pairs        =       av-pair *(";" av-pair)
av-pair         =       attr ["=" value]        ; optional value
attr            =       token
value           =       word
word            =       token | quoted-string

Attributes (names), which is attr, are case-insensitive ๐Ÿ™Œ. White space is allowed between tokens ๐Ÿซง. Even though the syntax above shows value as optional, most attrs need one ๐Ÿค.

NOTE: The syntax above allows whitespace between the attribute and the = sign. ๐Ÿ‘€

4.2. Origin Server Role ๐Ÿ‘‘

4.2.1. General โœจ

The origin server starts a session if it wants to ๐ŸŒธ. (Quick note: "session" here doesn't mean a persistent network connection. It means a logical session built from HTTP requests and responses. Whether there's a persistent connection or not should have no effect on cookie-derived sessions ๐Ÿ™ƒ.) To start one, the origin server sends back an extra response header to the client, Set-Cookie. (The details come later ๐Ÿ”œ.)

A user agent sends a Cookie request header (see below) to the origin server if it decides to keep a session going ๐Ÿ’–. The origin server can ignore that header or use it to figure out the current state of the session ๐Ÿ•ต๏ธโ€โ™€๏ธ. It can send back a Set-Cookie response header with the same or different info, or it can send no Set-Cookie header at all ๐ŸŽ€. The origin server ends a session by sending the client a Set-Cookie header with Max-Age=0 ๐Ÿ’€.

Servers can return Set-Cookie response headers with any response ๐ŸŒˆ. User agents should send Cookie request headers with every request, subject to the other rules detailed below ๐Ÿซถ.

An origin server can include multiple Set-Cookie headers in one response ๐Ÿ’…. Heads up: an intervening gateway could fold multiple such headers into a single header ๐Ÿ“.

Here's the grammar for the Set-Cookie response header ๐Ÿ“œ:

set-cookie      =       "Set-Cookie:" cookies
cookies         =       1#cookie
cookie          =       NAME "=" VALUE *(";" cookie-av)
NAME            =       attr
VALUE           =       value
cookie-av       =       "Comment" "=" value
                |       "Domain" "=" value
                |       "Max-Age" "=" value
                |       "Path" "=" value
                |       "Secure"
                |       "Version" "=" 1*DIGIT

Informally, the Set-Cookie response header is the token Set-Cookie:, followed by a comma-separated list of one or more cookies ๐Ÿง. Each cookie starts with a NAME=VALUE pair, followed by zero or more semi-colon-separated attribute-value pairs ๐Ÿ’–. The attribute-value pair syntax was shown earlier, and the specific attributes and what their values mean comes next โœจ. The NAME=VALUE attribute-value pair must come first in each cookie ๐Ÿ‘‘. The others, if present, can show up in any order ๐ŸŒˆ. If an attribute appears more than once in a cookie, the behavior is undefined ๐Ÿ’€.

NAME=VALUE ๐ŸŽ€ Required. The name of the state information ("cookie") is NAME, and its value is VALUE. NAMEs that begin with $ are reserved for other uses and must not be used by applications ๐Ÿ™…โ€โ™€๏ธ.

The VALUE is opaque to the user agent and can be anything the origin server chooses to send, possibly in a server-selected printable ASCII encoding ๐Ÿ”. "Opaque" means the content is only of interest and relevance to the origin server ๐Ÿ‘€. The content may, in fact, be readable by anyone who looks at the Set-Cookie header ๐Ÿ˜ญ.

Comment=comment ๐Ÿ“ Optional. Because cookies can contain private info about a user, the Cookie attribute lets an origin server document its intended use of a cookie ๐Ÿ•ต๏ธ. The user can inspect that info to decide whether to start or continue a session with this cookie ๐Ÿซถ.

Domain=domain ๐ŸŒ Optional. The Domain attribute specifies the domain for which the cookie is valid. An explicitly specified domain must always start with a dot ๐Ÿ’….

Max-Age=delta-seconds โณ Optional. The Max-Age attribute defines the lifetime of the cookie, in seconds. The delta-seconds value is a decimal non-negative integer. After delta-seconds seconds elapse, the client should discard the cookie. A value of zero means the cookie should be discarded immediately ๐Ÿ’€.

Path=path ๐Ÿ›ค๏ธ Optional. The Path attribute specifies the subset of URLs to which this cookie applies ๐Ÿฆ‹.

Secure ๐Ÿ”’ Optional. The Secure attribute (with no value) directs the user agent to use only (unspecified) secure means to contact the origin server whenever it sends back this cookie ๐Ÿซถ.

The user agent (possibly under the user's control) may decide what level of security it considers appropriate for "secure" cookies ๐Ÿ”. The Secure attribute should be considered security advice from the server to the user agent, indicating that it is in the session's interest to protect the cookie contents ๐Ÿšจ.

Version=version ๐ŸŒธ Required. The Version attribute, a decimal integer, identifies which version of the state management specification the cookie conforms to. For this specification, Version=1 applies ๐Ÿ’œ.

4.2.3. Controlling Caching ๐Ÿง

okay so origin servers have to be cognizant of caching, and that goes for both the returned resource AND the Set-Cookie header ๐Ÿ‘€ caching public documents is desirable, bestie. ๐ŸŒธ like if your server wants a public "front door" page to act as a sentinel for the start of a session, the page should be stored in caches "pre-expired" so the origin server keeps getting to see the further requests โœจ "Private documents," the ones with info strictly private to a session, should not be cached in shared caches ๐Ÿ™ˆ

if the cookie is meant for one user, the Set-cookie header should not be cached ๐Ÿ’œ but a Set-cookie header meant to be shared by multiple users may be cached, no cap ๐Ÿซถ

the origin server should send these extra HTTP/1.1 response headers depending on the situation:

  • to suppress caching of the Set-Cookie header: Cache-control: no-cache="set-cookie" ๐Ÿ™…โ€โ™€๏ธ

and then ONE of these:

  • to suppress caching of a private document in shared caches: Cache-control: private ๐Ÿ”’
  • to allow caching of a document but require it be validated before it gets returned to the client: Cache-control: must-revalidate ๐Ÿ”
  • to allow caching of a document but require that proxy caches (not user agent caches) validate it before returning it to the client: Cache-control: proxy-revalidate ๐Ÿ›ก๏ธ
  • to allow caching of a document and request that it be validated before returning it to the client (by "pre-expiring" it): Cache-control: max-age=0. not all caches will revalidate the document in every case, so don't get too comfy ๐Ÿ’€

HTTP/1.1 servers must send Expires: old-date (where old-date is a date long in the past) on responses containing Set-Cookie response headers, UNLESS they know for certain (by out of band means) that there are no downsteam HTTP/1.0 proxies ๐Ÿ•ฐ๏ธ HTTP/1.1 servers may send other Cache-Control directives that permit caching by HTTP/1.1 proxies on top of the Expires: old-date directive. the Cache-Control directive will override the Expires: old-date for HTTP/1.1 proxies, so it's giving priority, periodt ๐Ÿ‘‘


4.3. User Agent Role ๐Ÿฆ‹

the user agent keeps separate track of state info that arrives via Set-Cookie response headers from each origin server (as distinguished by name or IP address and port) ๐Ÿ—‚๏ธ and it applies these defaults for optional attributes that are missing:

Version Defaults to "old cookie" behavior as originally specified by
        Netscape.  See the HISTORICAL section.

Domain  Defaults to the request-host.  (Note that there is no dot at
        the beginning of request-host.)

Max-Age The default behavior is to discard the cookie when the user
        agent exits.

Path    Defaults to the path of the request URL that generated the
        Set-Cookie response, up to, but not including, the
        right-most /.

Secure  If absent, the user agent may send the cookie over an
        insecure channel.

4.3.2. Rejecting Cookies ๐Ÿšซ

to prevent possible security or privacy violations, a user agent rejects a cookie (it shall not store its information) if any of the following is true ๐Ÿ›‘

  • the value for the Path attribute is not a prefix of the request-URI.
  • the value for the Domain attribute contains no embedded dots or does not start with a dot.
  • the value for the request-host does not domain-match the Domain attribute.
  • the request-host is a FQDN (not IP address) and has the form HD, where D is the value of the Domain attribute, and H is a string that contains one or more dots.

examples, because we love a receipt ๐Ÿงพ

  • a Set-Cookie from request-host y.x.foo.com for Domain=.foo.com would be rejected, because H is y.x and contains a dot. ๐Ÿ™…โ€โ™€๏ธ
  • a Set-Cookie from request-host x.foo.com for Domain=.foo.com would be accepted. โœจ
  • a Set-Cookie with Domain=.com or Domain=.com. will always be rejected, because there is no embedded dot. ๐Ÿ’€
  • a Set-Cookie with Domain=ajax.com will be rejected because the value for Domain does not begin with a dot. ๐Ÿ˜ญ

if a user agent gets a Set-Cookie response header whose NAME is the same as a pre-existing cookie, AND whose Domain and Path attribute values exactly (string) match those of a pre-existing cookie, the new cookie supersedes the old ๐Ÿ”„ however, if the Set-Cookie has a value for Max-Age of zero, the (old and new) cookie is discarded ๐Ÿ—‘๏ธ otherwise cookies accumulate until they expire (resources permitting), at which point they get discarded ๐Ÿ•ฐ๏ธ

user agents have finite space to store cookies, so they may also discard older cookies to make room for newer ones, using, for example, a least-recently-used algorithm, along with constraints on the maximum number of cookies that each origin server may set ๐Ÿง 

if a Set-Cookie response header includes a Comment attribute, the user agent should store that info in a human-readable form with the cookie, and should display the comment text as part of a cookie inspection user interface ๐Ÿ”

user agents should allow the user to control cookie destruction ๐Ÿ‘‘ an infrequently-used cookie may work as a "preferences file" for network applications, and a user may want to keep it even if it's the least-recently-used cookie ๐Ÿ’– one possible implementation would be an interface that allows permanent storage of a cookie through a checkbox (or, conversely, its immediate destruction) โ˜‘๏ธ

privacy considerations dictate that the user have considerable control over cookie management. the PRIVACY section has more info ๐Ÿซถ

4.3.4. Sending Cookies to the Origin Server ๐Ÿ“ฎ

when it sends a request to an origin server, the user agent sends a Cookie request header to the origin server if it has cookies that are applicable to the request, based on:

  • the request-host;
  • the request-URI;
  • the cookie's age.

the syntax for the header is:

cookie          =       "Cookie:" cookie-version
                        1*((";" | ",") cookie-value)
cookie-value    =       NAME "=" VALUE [";" path] [";" domain]
cookie-version  =       "$Version" "=" value
NAME            =       attr
VALUE           =       value
path            =       "$Path" "=" value
domain          =       "$Domain" "=" value

the value of the cookie-version attribute must be the value from the Version attribute, if any, of the corresponding Set-Cookie response header. otherwise the value for cookie-version is 0. the value for the path attribute must be the value from the Path attribute, if any, of the corresponding Set-Cookie response header. otherwise the attribute should be omitted from the Cookie request header. the value for the domain attribute must be the value from the Domain attribute, if any, of the corresponding Set-Cookie response header. otherwise the attribute should be omitted from the Cookie request header ๐Ÿ“‹

note that there is no Comment attribute in the Cookie request header corresponding to the one in the Set-Cookie response header. the user agent does not return the comment info to the origin server ๐Ÿค

the following rules apply to choosing applicable cookie-values from among all the cookies the user agent has ๐ŸŽฏ

  • Domain Selection: the origin server's fully-qualified host name must domain-match the Domain attribute of the cookie.
  • Path Selection: the Path attribute of the cookie must match a prefix of the request-URI.
  • Max-Age Selection: cookies that have expired should have been discarded and thus are not forwarded to an origin server. โฐ

if multiple cookies satisfy the criteria above, they are ordered in the Cookie header such that those with more specific Path attributes precede those with less specific. ordering with respect to other attributes (e.g., Domain) is unspecified ๐Ÿคทโ€โ™€๏ธ

Note: for backward compatibility, the separator in the Cookie header is semi-colon (;) everywhere. a server should also accept comma (,) as the separator between cookie-values for future compatibility ๐Ÿ”ฎ

4.3.5. Sending Cookies in Unverifiable Transactions ๐Ÿ•ต๏ธ

users must have control over sessions in order to ensure privacy. (see PRIVACY section below.) to simplify implementation and to prevent an extra layer of complexity where adequate safeguards already exist, though, this document distinguishes between transactions that are verifiable and those that are unverifiable ๐Ÿงฉ a transaction is verifiable if the user has the option to review the request-URI before it's used in the transaction. a transaction is unverifiable if the user does not have that option. unverifiable transactions typically come up when a user agent automatically requests inlined or embedded entities, or when it resolves redirection (3xx) responses from an origin server ๐Ÿ”€ typically the origin transaction (the one the user initiates) is verifiable, and that transaction may directly or indirectly induce the user agent to make unverifiable transactions ๐Ÿซ 

when it makes an unverifiable transaction, a user agent must enable a session only if a cookie with a domain attribute D was sent or received in its origin transaction, such that the host name in the Request-URI of the unverifiable transaction domain-matches D. ๐Ÿšฆ

this restriction stops a malicious service author from using unverifiable transactions to induce a user agent to start or continue a session with a server in a different domain. starting or continuing such sessions could go against the user's privacy expectations, and could also be a security problem ๐Ÿ˜ฑ

user agents may offer configurable options that let the user agent, or any autonomous programs that the user agent executes, ignore the rule above, as long as these override options default to "off" ๐ŸŽ›๏ธ

plenty of current user agents already have a review option that would make many links verifiable ๐Ÿ‘€ for instance, some user agents display the URL that would be referenced for a particular link when the mouse pointer hovers over that link, so the user can decide whether to visit that site before the browser does it for them ๐Ÿ–ฑ๏ธ (though not implemented on current user agents, a similar technique could be used for a button that submits a form. the user agent could display the action to be taken if the user selected that button.) however, even this wouldn't make ALL links verifiable. for example, links to automatically loaded images wouldn't normally be subject to "mouse pointer" verification ๐Ÿ“ธ

many user agents also let a user view the HTML source of a document, or save the source to an external file where another application can view it. while that does give a crude review mechanism, some users might not consider it acceptable for this purpose ๐Ÿค“

okay so a user agent sends back most of the stuff from the Set-Cookie header to the origin server whenever the Path attribute matches a new request ๐Ÿ’Œ when the origin server gets a Cookie header, it should treat cookies whose NAME starts with $ specially, like an attribute for the cookie right next to it โœจ the value for that NAME applies to the lexically (left-to-right) most recent cookie whose name doesn't have the $ prefix. if there's no previous cookie, the value applies to the cookie mechanism as a whole ๐Ÿซถ here's an example:

Cookie: $Version="1"; Customer="WILE_E_COYOTE";
        $Path="/acme"

$Version applies to the cookie mechanism as a whole (and it gives the version number for the cookie mechanism) ๐Ÿ”ฅ $Path is an attribute whose value (/acme) defines the Path attribute that was used when the Customer cookie was defined in a Set-Cookie response header ๐Ÿ’…


4.5. Caching Proxy Role ๐Ÿฆ‹

one reason to keep state info separate from both a URL and the document content is so caching can scale ๐ŸŒธ to support cookies, a caching proxy must obey these rules that are already in the HTTP specification ๐Ÿงš

  • Honor requests from the cache, if possible, based on cache validity rules.
  • Pass along a Cookie request header in any request that the proxy must make of another server.
  • Return the response to the client. Include any Set-Cookie response header.
  • Cache the received response subject to the control of the usual headers, such as Expires, Cache-control: no-cache, and Cache-control: private,
  • Cache the Set-Cookie subject to the control of the usual header, Cache-control: no-cache="set-cookie". (The Set-Cookie header should usually not be cached.)

proxies must not introduce Set-Cookie (Cookie) headers of their own in proxy responses (requests) ๐Ÿ™ˆ


5. EXAMPLES โœจ

5.1. Example 1 ๐Ÿ’–

most of the request and response headers got cut for this one, so just assume the user agent has no stored cookies ๐Ÿ“

     1.  User Agent -> Server

         POST /acme/login HTTP/1.1
         [form data]

         User identifies self via a form.

     2.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"

         Cookie reflects user's identity.

     3.  User Agent -> Server

         POST /acme/pickitem HTTP/1.1
         Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
         [form data]

         User selects an item for "shopping basket."

     4.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
                 Path="/acme"

         Shopping basket contains an item.

     5.  User Agent -> Server

         POST /acme/shipping HTTP/1.1
         Cookie: $Version="1";
                 Customer="WILE_E_COYOTE"; $Path="/acme";
                 Part_Number="Rocket_Launcher_0001"; $Path="/acme"
         [form data]

         User selects shipping method from form.

     6.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Shipping="FedEx"; Version="1"; Path="/acme"

         New cookie reflects shipping method.

     7.  User Agent -> Server

         POST /acme/process HTTP/1.1
         Cookie: $Version="1";
                 Customer="WILE_E_COYOTE"; $Path="/acme";
                 Part_Number="Rocket_Launcher_0001"; $Path="/acme";
                 Shipping="FedEx"; $Path="/acme"
         [form data]

         User chooses to process order.

     8.  Server -> User Agent

         HTTP/1.1 200 OK

         Transaction is complete.

the user agent keeps making requests to the origin server, and after each one it gets a brand new cookie ๐Ÿ›’ all the cookies share the same Path attribute and the (default) domain. because every request URL starts with /acme, which matches the Path attribute, each request carries all the cookies received so far ๐Ÿ‘‘

5.2. Example 2 ๐ŸŒˆ

this one shows how the Path attribute works. all the request and response header details got cut, so assume the user agent has no stored cookies ๐Ÿฆ„

imagine the user agent already got these response headers from earlier requests:

Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
        Path="/acme"

and

Set-Cookie: Part_Number="Riding_Rocket_0023"; Version="1";
        Path="/acme/ammo"

a later request from the user agent to the (same) server for URLs like /acme/ammo/... would include this request header:

Cookie: $Version="1";
        Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
        Part_Number="Rocket_Launcher_0001"; $Path="/acme"
notice that the NAME=VALUE pair for the cookie with the more specific Path attribute, /acme/ammo, shows up before the one with the less specific Path attribute, /acme ๐Ÿ‘€ also notice that the same cookie name shows up more than once ๐Ÿ’…

a later request from the user agent to the (same) server for a URL like /acme/parts/ would include this request header:

Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"

here, the second cookie's Path attribute, /acme/ammo, isn't a prefix of the request URL, /acme/parts/, so that cookie doesn't get forwarded to the server ๐Ÿ™…โ€โ™€๏ธ


6. IMPLEMENTATION CONSIDERATIONS ๐Ÿ› ๏ธ

here we speculate on likely or desirable details for an origin server that implements state management ๐Ÿ”ฎ

an origin server's content should probably be split into disjoint application areas, and some of those need state info ๐Ÿง the application areas can be told apart by their request URLs. the Set-Cookie header can cover those areas by setting the Path attribute for each one ๐ŸŽ€

the session info can obviously be clear or encoded text that describes state. but if it grows too large, it gets unwieldy ๐Ÿ˜ญ so an implementor might choose for the session info to be a key to a server-side resource. of course, using a database brings some problems that this state management specification was meant to avoid, namely:

  1. keeping real state on the server side;
  2. how and when to garbage-collect the database entry, in case the user agent ends the session by, for example, exiting.

6.2. Stateless Pages ๐ŸŒท

caching is good for the scalability of the WWW, so it's important to reduce the number of documents that have state embedded in them inherently โœจ for example, if a shopping-basket-style app always shows a user's current basket contents on each page, those pages can't be cached, because each user's basket contents would be different ๐Ÿงบ on the other hand, if each page just has a link that lets the user "Look at My Shopping Basket", the page can be cached ๐Ÿ™Œ

6.3. Implementation Limits ๐Ÿงฎ

practical user agent implementations have limits on the number and size of cookies they can store. in general, user agents' cookie support should have no fixed limits ๐Ÿ’ซ they should strive to store as many frequently-used cookies as possible. furthermore, general-use user agents should provide each of the following minimum capabilities individually, although not necessarily simultaneously:

  • at least 300 cookies
  • at least 4096 bytes per cookie (as measured by the size of the characters that comprise the cookie non-terminal in the syntax description of the Set-Cookie header)
  • at least 20 cookies per unique host or domain name

user agents created for specific purposes or for limited-capacity devices should provide at least 20 cookies of 4096 bytes, so the user can interact with a session-based origin server ๐Ÿงšโ€โ™€๏ธ

the information in a Set-Cookie response header must be retained in its entirety. if there's inadequate space to store the cookie for some reason, it must be discarded, not truncated ๐Ÿšซโœ‚๏ธ

applications should use as few and as small cookies as possible, and they should cope gracefully with the loss of a cookie ๐Ÿซง

6.3.1. Denial of Service Attacks ๐Ÿ’€

user agents may choose to set an upper bound on the number of cookies stored from a given host or domain name, or on the size of the cookie info. otherwise a malicious server could try to flood a user agent with lots of cookies, or large cookies, on successive responses, which would force out cookies the user agent got from other servers ๐Ÿ˜ค however, the minima specified above should still be supported ๐Ÿ›ก๏ธ


7. PRIVACY ๐Ÿ”’

7.1. User Agent Control ๐Ÿฆ‹

an origin server could create a Set-Cookie header to track the path of a user through the server. users may object to this as an intrusive pile of info, even if their identity isn't evident ๐Ÿ™ˆ (identity might become evident if a user later fills out a form that has identifying info.) this state management specification therefore requires that a user agent give the user control over that possible intrusion, although the interface for that control is left unspecified ๐ŸŽ›๏ธ however, the control mechanisms provided shall at least allow the user:

  • to completely disable the sending and saving of cookies.
  • to determine whether a stateful session is in progress.
  • to control the saving of a cookie on the basis of the cookie's Domain attribute.

that control could be provided by, for example, mechanisms:

  • to notify the user when the user agent is about to send a cookie to the origin server, offering the option not to begin a session.
  • to display a visual indication that a stateful session is in progress.
  • to let the user decide which cookies, if any, should be saved when the user closes a window or ends a user agent session.
  • to let the user examine the contents of a cookie at any time.

a user agent usually begins execution with no remembered state info. it should be possible to configure a user agent never to send Cookie headers, in which case it can never sustain state with an origin server. (the user agent would then behave like one that doesn't know how to handle Set-Cookie response headers ๐Ÿฅฒ)

when the user agent stops running, it should let the user discard all state info. alternatively, the user agent may ask the user whether state info should be kept; the default should be "no". if the user chooses to keep state info, it would be restored the next time the user agent runs ๐Ÿ”

NOTE: user agents should probably be cautious about using files to store cookies long-term. if a user runs more than one instance of the user agent, the cookies could get commingled or otherwise messed up ๐Ÿ˜ฌ

7.2. Protocol Design ๐Ÿงฉ

the restrictions on the value of the Domain attribute, and the rules about unverifiable transactions, are meant to cut down on the ways cookies can "leak" to the "wrong" site ๐Ÿšจ the intent is to restrict cookies to one host, or a closely related set of hosts. so a request-host is limited in what values it can set for Domain. we consider it acceptable for hosts host1.foo.com and host2.foo.com to share cookies, but not a.com and b.com ๐Ÿ™…โ€โ™€๏ธ

similarly, a server can only set a Path for cookies that are related to the request-URI ๐Ÿ’…


8. SECURITY CONSIDERATIONS ๐Ÿ”

8.1. Clear Text ๐Ÿ”“

Okay, bestie, this one is a little scary. The information in the Set-Cookie and Cookie headers is unprotected, so anyone can read it and anyone in the middle can mess with it ๐Ÿ˜ญ Two things happen because of that:

  1. Any sensitive information carried in them is exposed to intruders. ๐Ÿ‘€
  2. A malicious intermediary could alter the headers as they travel in either direction, with unpredictable results. ๐Ÿ’€

So the vibe check is: personal or financial info should only be sent over a secure channel ๐Ÿ”. For less sensitive stuff, or when the header content is a database key, the origin server should be vigilant so a bad Cookie value can't cause failures. Protect your babies! ๐Ÿซถ

Proper app design can stop spoofing attacks from related domains, and this little scenario is giving villain arc energy ๐ŸŽญ Picture this:

  1. User agent makes a request to victim.cracker.edu, gets back cookie session_id="1234", and sets the default domain victim.cracker.edu.
  2. User agent makes a request to spoof.cracker.edu, gets back cookie session-id="1111", with Domain=".cracker.edu".
  3. User agent makes a request to victim.cracker.edu again, and passes:
Cookie: $Version="1";
                session_id="1234";
                session_id="1111"; $Domain=".cracker.edu"

The server at victim.cracker.edu should notice that the second cookie wasn't one it originated, because its Domain attribute isn't for itself, and then ignore it. Caught you lacking, spoofer! ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’…

A user agent should make every attempt to stop session info from being shared between hosts in different domains. This is a major privacy red flag ๐Ÿšฉ Embedded or inlined objects can cause seriously bad privacy problems if they're used to share cookies between totally unrelated hosts. For example, a malicious server could stash cookie info for a.com inside a URI for a CGI on b.com. User agent implementors are strongly encouraged to block this kind of exchange whenever they can. Shut it down, no notes! ๐Ÿ›‘โœจ


9. OTHER, SIMILAR, PROPOSALS ๐ŸŒˆ

Okay, so there were three other proposals trying to do similar stuff ๐Ÿ‘€ This spec is basically a blend of Kristol's State-Info proposal and Netscape's Cookie proposal. Iconic mix ๐Ÿ’œ

Brian Behlendorf proposed a Session-ID header that the user agent would start and that an origin server could use to track "clicktrails" ๐Ÿž It wouldn't carry any origin-server-defined state though. Phillip Hallam-Baker also proposed another client-defined session ID mechanism for similar purposes โœจ

Here's the tea โ˜•: session IDs and cookies can both keep stateful sessions going, but they're for different things, so their privacy requirements are different too ๐Ÿ” A user starts session IDs so servers can track their progress, or tell apart multiple users on a shared machine. Cookies are server-initiated, though, so the cookie mechanism described here gives users control over something that would otherwise happen without them even knowing ๐Ÿ‘๏ธ Plus, cookies carry rich, server-selected info, while session IDs are just simple, user-selected info. Totally different energy ๐Ÿงš


10. HISTORICAL ๐Ÿ•ฐ๏ธ

10.1. Compatibility With Netscape's Implementation ๐Ÿฆ–

HTTP/1.0 clients and servers may use Set-Cookie and Cookie headers that reflect Netscape's original cookie proposal. These notes are about how "old" and "new" cookies inter-operate, so get ready for some throwback lore ๐Ÿ“ผ

This proposal adds attribute-value pairs to the Cookie request header in a compatible way. An "old" client that gets a "new" cookie will ignore attributes it doesn't understand, and it returns whatever it does understand to the origin server. A "new" client always sends cookies in the new form ๐Ÿ’…

An "old" server that gets a "new" cookie will see what it thinks are a bunch of cookies with names starting with $, and it'll ignore them. (The "old" server expects these cookies to be separated by semi-colon, not comma.) A "new" server can spot cookies that passed through an "old" client, because they lack a $Version attribute ๐Ÿ”Ž

10.1.2. Expires and Max-Age โฐ

Netscape's original proposal defined an Expires header that took a date value in a fixed-length variant format, in place of Max-Age:

Wdy, DD-Mon-YY HH:MM:SS GMT

Heads up, this date format has embedded spaces, and "old" cookies didn't have quotes around their values. Clients implementing to this spec should be aware of "old" cookies and Expires ๐Ÿซ 

10.1.3. Punctuation โœ๏ธ

In Netscape's original proposal, the values in attribute-value pairs didn't accept "-quoted strings. Origin servers should be cautious about sending values that need quotes, unless they know the receiving user agent understands them (i.e., "new" cookies). A ("new") user agent should only use quotes around values in Cookie headers when the cookie's version(s) are all compliant with this specification or later.

Also, in Netscape's original proposal, no whitespace was allowed around the = that separates attribute-value pairs. So be careful with whitespace in new implementations, okay? ๐Ÿ™ƒ

10.2. Caching and HTTP/1.0 ๐ŸงŠ

Some caches, like the ones conforming to HTTP/1.0, will inevitably cache the Set-Cookie header, because there was no way to stop caching headers before HTTP/1.1. This can lead to security problems ๐Ÿ˜ฌ Documents an origin server sends along with Set-Cookie headers will usually be either uncachable or "pre-expired". As long as caches obey instructions not to cache documents (following Expires: <a date in the past>, or Pragma: no-cache (HTTP/1.0), or Cache-control: no-cache (HTTP/1.1)), uncachable documents are no problem.

Pre-expired documents, though, may get stored in caches. They need validation (a conditional GET) on each new request, but some cache operators loosen the rules for their caches and sometimes serve expired documents without validating them first. That combo can lead to cookies meant for one user later being sent to another user ๐Ÿ˜ญ The Set-Cookie header gets stored in the cache, and even though the document is stale (expired), the cache returns it in response to later requests, including the cached headers. Not cute! ๐Ÿšซ


11. ACKNOWLEDGEMENTS ๐Ÿซถ

This document really represents the collective efforts of the following people, in addition to the authors: Roy Fielding, Marc Hedlund, Ted Hardie, Koen Holtman, Shel Kaphan, Rohit Khare. Thank you, queens! ๐Ÿ‘‘๐Ÿ’–

12. AUTHORS' ADDRESSES ๐Ÿ“ฌ

David M. Kristol
Bell Laboratories, Lucent Technologies
600 Mountain Ave.  Room 2A-227
Murray Hill, NJ  07974

Phone: (908) 582-2250
Fax: (908) 582-5809
EMail: dmk@bell-labs.com
Lou Montulli
Netscape Communications Corp.
501 E. Middlefield Rd.
Mountain View, CA  94043

Phone: (415) 528-2600
EMail: montulli@netscape.com
๐Ÿ“ฆ where this page comes from