The two cookie headers
Cookies travel in two different headers depending on direction:
Set-Cookie— sent by the server, one cookie per header line (or multipleSet-Cookieheaders). Includes the value plus attributes (Path, Domain, Expires, Max-Age, Secure, HttpOnly, SameSite, Partitioned).Cookie— sent by the client, just name=value pairs separated by semicolons. The client doesn't echo back the attributes — only the names and values.
A typical Set-Cookie header from a server:
Set-Cookie: session_id=abc123def456; Path=/; Domain=example.com;
Expires=Wed, 25 May 2026 10:30:00 GMT; Max-Age=3600;
Secure; HttpOnly; SameSite=Lax
And the corresponding Cookie header the client sends back:
Cookie: session_id=abc123def456; theme=dark; analytics_id=xyz789
What each attribute does
Path— the cookie only gets sent for requests to URLs under this path. Default is the URL path of the request that set it. UsePath=/for site-wide cookies.Domain— which domain(s) the cookie applies to. Default is the exact host.Domain=example.comincludes subdomains.Expires— an absolute date when the cookie expires. Cookies without Expires (and without Max-Age) are "session cookies" — deleted when the browser closes.Max-Age— relative expiration, in seconds. Takes precedence over Expires when both are present.Max-Age=0deletes the cookie.Secure— only send over HTTPS. Always include this in production. Required for cookies withSameSite=Noneper modern browsers.HttpOnly— not accessible to JavaScript viadocument.cookie. Critical defense against XSS-based session theft. Should be on every authentication cookie.SameSite— controls when the cookie is sent on cross-site requests.Strict= never on cross-site;Lax= on top-level navigation only (default in modern browsers);None= always (requires Secure).Partitioned— newer attribute (CHIPS, "Cookies Having Independent Partitioned State") — partitions third-party cookies by top-level site. Defends against cross-site tracking via cookies.
The right defaults for new cookies
For 2026, a sensible default for authentication cookies:
Set-Cookie: session_id=<value>;
Path=/;
Max-Age=86400;
Secure;
HttpOnly;
SameSite=Lax
For cookies that need to work across cross-site iframes (third-party contexts):
Set-Cookie: cross_origin_token=<value>;
Path=/;
Max-Age=86400;
Secure;
HttpOnly;
SameSite=None;
Partitioned
Don't include Domain unless you specifically need the cookie shared across subdomains. Without Domain, the cookie defaults to the exact host that set it — usually what you want.
Common parsing pitfalls
- Multiple Set-Cookie headers, not one combined. HTTP allows repeated headers; servers send one Set-Cookie per cookie. Naive parsers that split on commas break (commas appear in Expires dates).
- The Cookie header is semicolon-separated, but the value can contain anything. Quoted strings with embedded equals signs and semicolons are valid. Most parsers handle this poorly.
- SameSite=None requires Secure. Browsers reject SameSite=None cookies that aren't marked Secure. This breaks third-party login flows on HTTP test environments.
- HttpOnly cookies are invisible to
document.cookie. If you can't see a cookie in JavaScript but the server reports setting it, check whether HttpOnly is on (it should be, for auth cookies).
Common use cases
- Debug authentication issues by inspecting cookie attributes
- Audit cookies for security misconfigurations before shipping
- Understand third-party cookie behavior with SameSite and Partitioned
- Decode a cookie string copied from browser DevTools
Frequently asked questions
Why is SameSite=Lax the default in modern browsers?
Browsers (Chrome since 2020, others followed) default cookies to SameSite=Lax when no SameSite attribute is set. This stops cookies from being sent on cross-site POSTs (CSRF protection) while still allowing top-level cross-site GETs (links from one site to another). It's a security win at the cost of some legacy cross-site flows.
When do I need SameSite=None?
When your cookie needs to work in cross-site iframes or POST requests from other origins. Examples: third-party authentication providers, embedded payment widgets, analytics that span domains. SameSite=None requires Secure (HTTPS), and modern browsers also recommend Partitioned for these cases.
What is Partitioned (CHIPS)?
Partitioned (Cookies Having Independent Partitioned State) splits a third-party cookie's storage by the top-level site it was set on. So example.com's embedded widget on site-a.com gets a different cookie jar than on site-b.com. This breaks cross-site tracking but keeps legitimate same-context flows working.
Should every cookie be HttpOnly?
Every auth cookie should be HttpOnly — it prevents JavaScript from reading the cookie, which means an XSS attack can't exfiltrate session tokens. Cookies that JavaScript legitimately needs to read (theme preferences, language, dismissed-notification flags) don't need HttpOnly, but they also usually don't need to be cookies at all (localStorage is better for those).