Learn how HTTP Cookies work
By Flavio Copes
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.
Cookie limits
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.
Limit a cookie by host and path
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.
Use cookie name prefixes
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 or delete a cookie
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.
Cookie security rules
For session cookies:
- generate an unpredictable session ID
- keep account data on the server
- use HTTPS and
Secure - use
HttpOnly - choose an explicit
SameSitevalue - use the smallest practical host and path scope
- rotate the session ID after login and privilege changes
- expire the session on the server, not only in the browser
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: