Cookie attributes guide: SameSite, Partitioned, HttpOnly, Secure, Domain, Path, Priority
Set-Cookie carries more than seven attributes, all of which interact with authentication, CSRF protection, and cross-site tracking enforcement. “Just set SameSite=Lax” or ”HttpOnly is enough” oversimplifies; with Partitioned arriving and third-party cookies dwindling, the right combination depends on use case. This article maps the attributes, then gives recommended combinations.
Attribute summary
Set-Cookie: sessionId=abc123; Domain=example.com; Path=/; Max-Age=3600;
Secure; HttpOnly; SameSite=Lax; Priority=High; Partitioned | Attribute | Role | Default |
|---|---|---|
Domain | Hostname scope | request origin |
Path | Path scope | request path |
Expires / Max-Age | Lifetime | session (cleared on tab close) |
Secure | HTTPS-only transmission | off (sent over HTTP too) |
HttpOnly | Hidden from JS | off (visible to document.cookie) |
SameSite | Cross-site send rules | Lax (modern browsers) |
Partitioned | CHIPS partitioning | off |
Priority | Eviction priority | Medium |
SameSite: the CSRF and tracking dial
The most important attribute. Controls cross-site send behavior.
| Value | Cross-site GET (link) | Cross-site POST (form) | Inside iframe |
|---|---|---|---|
Strict | ✗ | ✗ | ✗ |
Lax | ✓ | ✗ | ✗ |
None | ✓ | ✓ | ✓ (requires Secure) |
SameSite=Lax (modern default)
Since Chrome 80 (2020), an unspecified SameSite is treated as Lax.
- Same-site: always sent.
- Cross-site top-level GET (
<a href>click): sent. - Cross-site POST, iframe load, image load: not sent.
This is the primary CSRF defense — blocks “form POST from another site” attacks.
SameSite=Strict
Never sent on cross-site requests, including top-level GETs. A user clicking your site from a Google search arrives logged-out.
To preserve UX, the standard pattern is two cookies: a short-lived Strict cookie for sensitive operations and a long-lived Lax cookie for general session presence.
SameSite=None (requires Secure)
Sent in all cross-site contexts. Classic third-party cookie behavior.
Chrome 80+ rejects SameSite=None without Secure. HTTPS is mandatory.
Partitioned (CHIPS): replacement for third-party cookies
Set-Cookie: id=abc; Secure; SameSite=None; Partitioned CHIPS (Cookies Having Independent Partitioned State) gives third-party iframes cookies that are partitioned per top-level site.
Motivation
Some third-party cookie use cases are legitimate: chat widgets keeping conversation state, embedded payment auth, CMS preview tokens. Cross-site tracking (“same cookie value identifies the same user across sites”) is the harm. CHIPS keeps the legitimate uses while breaking tracking:
chat.example.comiframe embedded onsiteA.com→ cookie stored under siteA’s partition.- Same iframe on
siteB.com→ separate partition. chat.example.comcannot correlate users across siteA and siteB.
When to use
- Your own embeddable widgets (chat, comments, payment iframes).
- Legitimate cross-site state where tracking is not the goal.
If your cookie’s purpose is tracking, CHIPS doesn’t preserve the use case — you need First-Party Sets, Privacy Sandbox APIs, or a redesign.
HttpOnly and Secure
HttpOnly
Set-Cookie: sessionId=abc; HttpOnly - Cannot be read or written via
document.cookie. - Sent on HTTP requests as normal.
- The primary XSS defense for session cookies.
Always set HttpOnly on session IDs and auth tokens.
Secure
Set-Cookie: sessionId=abc; Secure - Sent only over HTTPS.
- Prevents network interception of cookie values.
- Mandatory with
SameSite=None.
For dev (HTTP localhost) vs prod (HTTPS):
const secure = process.env.NODE_ENV === 'production';
res.cookie('sessionId', value, { secure, httpOnly: true }); Domain and Path
Domain
Set-Cookie: id=abc; Domain=example.com The cookie is sent to example.com and all subdomains (api.example.com, www.example.com, …).
If omitted, the cookie is sent only to the exact host that set it (no subdomains). Specify a parent domain explicitly when you want subdomain sharing — and avoid otherwise to minimize the attack surface.
Path
Set-Cookie: id=abc; Path=/admin Sent only on requests under /admin. /admin/users ✓, /dashboard ✗.
In practice most cookies use Path=/. Path scoping is occasionally used to reduce header bloat on cookie-heavy sites.
Expires and Max-Age
Set-Cookie: id=abc; Max-Age=3600
Set-Cookie: id=abc; Expires=Thu, 01 Dec 2026 16:00:00 GMT | Unit | Reference | |
|---|---|---|
Max-Age | seconds | relative to receipt time |
Expires | UTC datetime | client’s clock |
Max-Age wins when both are set. Prefer Max-Age to avoid client-clock-skew issues.
Max-Age=0 or a past Expires is the delete instruction — re-send with the same name / Domain / Path.
Priority (Chrome extension)
Set-Cookie: id=abc; Priority=High Controls eviction order when the browser hits its cookie cap. Low / Medium (default) / High. Chrome-only; Firefox and Safari ignore it.
Rarely needed; useful when an auth cookie is being evicted on cookie-heavy sites.
Recommended combination for auth cookies
Set-Cookie: sessionId=abc;
Path=/;
Max-Age=3600;
Secure;
HttpOnly;
SameSite=Lax Path=/: site-wide.Max-Age=3600: 1 hour, refreshable.Secure: HTTPS only.HttpOnly: XSS-resistant.SameSite=Lax: CSRF-resistant.
Don’t set Domain unless you actually need subdomain sharing — leaving it off shrinks the attack surface.
Modern CSRF defense
SameSite=Lax alone has gaps:
- Modifying methods (PUT / DELETE) cross-site → not sent under
Lax. ✓ - GET endpoints with side effects (legacy APIs) → sent under
Lax. Vulnerable. - iframe-embedded apps → not sent under
Lax. ✓
The robust complement is the Double Submit Cookie pattern: store the CSRF token in a cookie, require the client to also echo it in a header / form value. Server compares them:
// Server sets a CSRF cookie (not HttpOnly — JS needs to read it)
Set-Cookie: csrfToken=xyz; Secure; SameSite=Lax
// Client reads the cookie and echoes the value
fetch('/api/transfer', {
headers: { 'X-CSRF-Token': getCsrfToken() }
}); Browser support differences
| Feature | Chrome | Firefox | Safari |
|---|---|---|---|
| SameSite=Lax default | ✓ | ✓ | ✓ |
Partitioned (CHIPS) | ✓ (118+) | ✓ (131+) | ✗ |
Priority | ✓ | ✗ | ✗ |
| Third-party cookie deprecation | paused | off by default | off by default (ITP) |
Safari’s ITP (Intelligent Tracking Prevention) is the strictest; Partitioned is not yet implemented. Designs that depend on third-party cookies should assume Safari users will lose the feature and degrade gracefully.
Summary
Cookie attributes split into four axes: who receives it (Domain / Path / SameSite), how it’s protected (Secure / HttpOnly), when it expires (Max-Age / Expires), and partitioning (Partitioned). The 2026-standard auth cookie is Secure + HttpOnly + SameSite=Lax; for cross-site widgets, SameSite=None + Secure + Partitioned. Plan for graceful degradation on browsers that lack Partitioned, especially Safari.