HTTP cookie

The Hypertext Transfer Protocol (HTTP) is fundamentally a stateless protocol. This means that every request a client makes to a server is treated as an independent transaction, with no inherent knowledge of previous interactions. Without an additional mechanism, a web server would "forget" who a user is the moment a page finishes loading, making features like user logins, shopping carts, or personalized settings impossible.

HTTP Cookies were designed to bridge this gap. They serve as a lightweight client-side state management mechanism, allowing servers to "remember" users by instructing the browser to store small pieces of data and return them with every subsequent request.

The interaction between a client (browser) and a server regarding cookies follows a specific, automated workflow:

  1. Initial Request: The client sends a standard HTTP request to the server.
  2. Server Response & Instruction: The server processes the request and, if it needs to track the user, includes a Set-Cookie header in its HTTP response.
  3. Client-Side Storage: The browser receives the response, reads the Set-Cookie instruction, and stores the data according to the specified attributes.
  4. Automatic Transmission: For all future requests to the same domain (subject to path and security constraints), the browser automatically attaches the stored data in the Cookie request header.
  5. State Recognition: The server reads the Cookie header, retrieves the data, and uses it to identify the session, restore preferences, or maintain a continuous user experience.

Practical Example

When a server wants to establish a session, it might send a response like this:

HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; Max-Age=3600; Path=/; Secure; HttpOnly; SameSite=Lax

In the next request to the same site, the browser will automatically include:

GET /account HTTP/1.1
Host: example.com
Cookie: session_id=abc123

Crucial Distinction: The attributes defined in Set-Cookie (like HttpOnly or Max-Age) are instructions for the browser. These attributes are not sent back to the server in the Cookie request header; only the name=value pairs are transmitted.

To control how a cookie behaves, developers use various attributes within the Set-Cookie header. These attributes define the cookie's scope, lifespan, and security posture.

Scoping and Identity

  • Name=Value: The core payload. The name and value are typically URL-encoded to ensure they don't contain illegal characters.
  • Domain: Specifies which hosts are allowed to receive the cookie. If omitted, it defaults to the current host only. If specified, it can include subdomains.
  • Path: Defines the URL path that must exist in the requested URL for the browser to send the cookie. Setting Path=/ makes the cookie available across the entire domain.

Lifespan and Expiration

  • Expires: Sets an absolute expiration date and time using the HTTP date format.
  • Max-Age: Sets a relative expiration time in seconds. If both Expires and Max-Age are present, Max-Age takes precedence.

Security and Privacy Controls

  • Secure: Ensures the cookie is only transmitted over encrypted HTTPS connections, preventing it from being intercepted in plain text over unencrypted networks.
  • HttpOnly: A vital defense against Cross-Site Scripting (XSS). When this flag is set, the cookie cannot be accessed through client-side JavaScript (e.g., document.cookie). This prevents attackers from stealing session tokens via malicious scripts.
  • SameSite: A modern attribute designed to mitigate Cross-Site Request Forgery (CSRF) by controlling whether cookies are sent with cross-site requests.
    • Strict: The cookie is only sent in a first-party context (when the site in the URL bar matches the cookie's domain).
    • Lax: The cookie is not sent on cross-site subrequests (like images or frames) but is sent when a user navigates to the origin site (e.g., following a link). This is the modern browser default.
    • None: The cookie is sent in all contexts. Note: Using SameSite=None requires the Secure attribute to be set.
  • Prefixes (__Secure- and __Host-): These are specialized prefixes that provide enhanced security. For instance, a cookie starting with __Host- must have the Secure flag, must have Path=/, and must not have a Domain attribute, effectively locking it to the specific host that set it.

Classification: Session vs. Persistent Cookies

Cookies are generally categorized by their longevity:

  • Session Cookies: These do not have an Expires or Max-Age attribute. They reside in the browser's temporary memory and are typically deleted when the user closes the browser session. These are ideal for sensitive, short-lived data like login sessions.
  • Persistent Cookies: These include an expiration date or a Max-Age. They are written to the user's disk and remain available even after the browser is closed, until they expire. These are commonly used for "Remember Me" features or user preferences (e.g., dark mode).

Managing Cookies via JavaScript

While HttpOnly cookies are invisible to JavaScript, developers can still manage non-sensitive cookies using the document.cookie API.

Writing a Cookie:

document.cookie = "theme=dark; Path=/; Max-Age=86400; SameSite=Lax";

Deleting a Cookie:
To delete a cookie, you cannot simply "remove" it. Instead, you must overwrite the existing cookie with the same name, path, and domain, but set its expiration date to the past (or set Max-Age to 0).

document.cookie = "theme=; Path=/; Max-Age=0; SameSite=Lax";

Security Best Practices

Because cookies are a primary target for attackers, following strict security principles is non-negotiable:

  1. Prioritize HttpOnly and Secure: Always use these flags for session identifiers and any sensitive tokens.
  2. Minimize Data Exposure: Never store sensitive information—such as passwords, plain-text API keys, or personally identifiable information (PII)—directly in a cookie.
  3. Leverage SameSite: Use Lax or Strict to defend against CSRF attacks, but remember that SameSite is a defense-in-depth measure and should be used alongside CSRF tokens.
  4. Use Host-Specific Prefixes: For critical security tokens, use the __Host- prefix to prevent subdomain hijacking.
  5. Defense Against XSS: While HttpOnly protects the cookie itself, it does not stop an XSS attack from performing actions on behalf of the user. Always sanitize inputs and use Content Security Policies (CSP).
  6. Privacy Compliance: When using cookies for tracking or advertising, ensure compliance with global regulations like GDPR or CCPA by obtaining proper user consent.

Limitations and Alternatives

Cookies are not a "catch-all" storage solution. They have several inherent limitations:

  • Size Constraints: Most browsers limit a single cookie to approximately 4KB.
  • Quantity Limits: There is a limit on the total number of cookies a single domain can store.
  • Network Overhead: Because cookies are automatically sent with every request to the domain, large cookies can significantly increase the size of your HTTP headers, leading to unnecessary bandwidth consumption and latency.

Depending on your use case, consider these alternatives:

  • localStorage: Offers much larger storage capacity and a more intuitive API, but it is fully accessible to JavaScript and thus vulnerable to XSS.
  • sessionStorage: Similar to localStorage but scoped to the specific browser tab/session.
  • IndexedDB: A powerful, client-side NoSQL database for storing large amounts of structured data.
  • Authorization Header: For modern APIs, it is often better to store tokens (like JWTs) in memory or secure storage and manually attach them to the Authorization header rather than relying on automatic cookie transmission.

Summary

HTTP Cookies remain a cornerstone of web development, providing the essential "memory" required for a modern, interactive web. By mastering the nuances of attributes like SameSite, HttpOnly, and Secure, and understanding when to opt for Web Storage instead, developers can build applications that are both seamless and resilient against common security threats.