Skip to content
FLAVIO COPES
flaviocopes.com

Learn how HTTP Cookies work

By

Learn how HTTP cookies move between browser and server, how scope and expiration work, and how Secure, HttpOnly, and SameSite protect them.

~~~

An HTTP cookie is a small name-value pair that a browser stores for a site.

Servers often use cookies to identify a session. The cookie contains an opaque session ID, while the server keeps the account and session data.

Cookies are also used for preferences and other small values. They are not a replacement for a database.

How cookies travel

A server sets a cookie in an HTTP response:

Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

The browser stores it. On later matching requests, the browser sends:

Cookie: session=abc123

The browser decides whether a cookie matches by checking its domain, path, expiration, Secure, SameSite, and partition rules.

Frontend JavaScript does not need to manually add the Cookie header. Browsers manage that header, and do not let scripts set it directly.

Cookies are intentionally small. Browser limits vary, but an individual cookie is commonly limited to about 4 KB.

Browsers also limit how many cookies an origin or domain can keep. Do not depend on one exact number.

Cookies add bytes to matching HTTP requests. Use Web Storage or IndexedDB for client-only data that does not need to reach the server.

Session and persistent cookies

A cookie without Expires or Max-Age is a session cookie:

Set-Cookie: theme=dark; Path=/

Session cookies normally last for the browser session. Browser session restore can preserve them across a restart, so closing the browser is not a reliable security boundary.

Use Max-Age to set a lifetime in seconds:

Set-Cookie: theme=dark; Max-Age=2592000; Path=/

This cookie can last for 30 days.

Alternatively, use an HTTP date with Expires:

Set-Cookie: theme=dark; Expires=Tue, 18 Aug 2026 12:00:00 GMT; Path=/

When both are present, Max-Age takes precedence.

If you omit Domain, the browser creates a host-only cookie. A cookie set by app.example.com then returns only to app.example.com, not to its subdomains or parent domain.

Set Domain=example.com only when the cookie must also reach subdomains:

Set-Cookie: preference=compact; Domain=example.com; Path=/

A site cannot set cookies for an unrelated domain or a public suffix such as com.

Leaving out Domain is the safer default because it gives the cookie a smaller scope.

Path limits which request paths receive the cookie:

Set-Cookie: dashboard-view=list; Path=/dashboard

This matches /dashboard and paths below it.

Path is a delivery rule, not a security boundary. Other code on the same origin may still be able to access the cookie.

Protect cookies with attributes

Secure

Secure tells the browser to send the cookie only over HTTPS:

Set-Cookie: session=abc123; Secure

Use HTTPS for the entire site. Secure protects cookie transport, but does not encrypt the value inside the browser or server.

HttpOnly

HttpOnly hides a cookie from JavaScript APIs such as document.cookie:

Set-Cookie: session=abc123; Secure; HttpOnly

Only a server can set HttpOnly through the Set-Cookie response header. This does not work: adding HttpOnly to a document.cookie assignment cannot create an HttpOnly cookie.

An XSS payload cannot read an HttpOnly session cookie. It may still perform actions as the signed-in user, so HttpOnly reduces impact but does not fix XSS.

SameSite

SameSite controls whether a cookie is sent with cross-site requests.

Strict gives the narrowest cross-site behavior:

Set-Cookie: session=abc123; SameSite=Strict

Lax also permits some top-level cross-site navigations:

Set-Cookie: session=abc123; SameSite=Lax

None permits cross-site use and requires Secure:

Set-Cookie: widget-session=abc123; SameSite=None; Secure

Modern browsers commonly treat a missing SameSite as Lax, with compatibility details. Set it explicitly so the intended behavior is clear.

SameSite helps defend against CSRF, but it is not the only CSRF control an application may need.

Partitioned

Partitioned requests partitioned storage for a third-party cookie. The browser keeps a separate cookie jar for each top-level site.

It requires Secure:

Set-Cookie: widget=compact; SameSite=None; Secure; Partitioned

Use this only when building a cross-site embedded feature that needs state.

Cookie prefixes let supporting browsers enforce attribute rules.

__Host- is useful for host-bound cookies:

Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

A __Host- cookie must use Secure, must use Path=/, and must not have a Domain attribute.

Prefixes add useful checks. They do not replace secure session design.

Read and write cookies in JavaScript

document.cookie exposes non-HttpOnly cookies available to the current document:

console.log(document.cookie)

The result is one string:

theme=dark; language=en

Writing to document.cookie adds or updates one cookie. It does not replace every cookie:

document.cookie = 'theme=dark; Path=/; Max-Age=2592000; SameSite=Lax; Secure'

Encode values that can contain unsupported characters:

const value = encodeURIComponent('dark mode')
document.cookie = `theme=${value}; Path=/; SameSite=Lax; Secure`

document.cookie is synchronous and can be awkward to parse. The asynchronous Cookie Store API is an option where your supported browsers provide it.

Authentication cookies should normally be server-set and HttpOnly, which means frontend JavaScript should not read them.

Update a cookie by setting the same name, domain, and path:

document.cookie = 'theme=light; Path=/; Max-Age=2592000; SameSite=Lax; Secure'

Delete it by using the same scope and a non-positive Max-Age:

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

If the path or domain does not match the original cookie, you create or delete a different cookie.

For session cookies:

Do not trust a cookie merely because the browser returned it. Users can modify non-HttpOnly cookies, and can send custom HTTP requests.

Signing a value can detect modification. Encryption can hide its contents. Neither one makes authorization checks optional.

See MDN’s guide to using HTTP cookies, the Set-Cookie reference, and the document.cookie reference.

~~~

Related posts about platform: