Frontend Labs
⋯

60. Cookies and state

60.1 The mechanics

Server -> client:   Set-Cookie: sid=abc; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=3600
Client -> server:   Cookie: sid=abc            (on every matching request, automatically)

Cookies are attached by the browser, automatically, based on scope — which is precisely why CSRF exists.

60.2 The attributes, and what they actually do

Attribute Effect
Domain=example.com Sent to example.com and all subdomains. Omitting Domain is narrower (host-only) and usually safer.
Path=/app Sent only for paths under /app. Not a security boundary (same-origin JS can read across paths).
Secure HTTPS only. Always set it.
HttpOnly Not readable by JavaScript — mitigates XSS token theft
SameSite=Strict Never sent on cross-site requests, including top-level navigation. Breaks "click a link from email and be logged in".
SameSite=Lax Sent on top-level GET navigation only. The modern default.
SameSite=None Sent on all cross-site requests — requires Secure. Needed for third-party embeds/SSO.
Max-Age / Expires Persistent; without either, it's a session cookie
__Host- prefix Browser enforces: Secure, Path=/, and no Domain. The strongest binding available.
__Secure- prefix Browser enforces Secure.

60.3 Scoping hazards

  • Cookies ignore ports and (mostly) scheme. A cookie set by http://example.com:8080 is sent to https://example.com:443. Cookies are not origin-scoped like the rest of the web platform.
  • Subdomain leakage: Domain=example.com sends your session cookie to untrusted-user-content.example.com. Use host-only cookies, and isolate user content on a different registrable domain, not a subdomain.
  • Cookie jar size limits (~4 KB per cookie, ~50 per domain) — exceed them and cookies are silently dropped.
  • Every cookie is sent on every request to that scope, including images and API calls. Large cookies are a real, measurable performance cost.

60.4 CSRF, and how SameSite changed it

Classic CSRF: an attacker's page issues a form POST to your bank; the browser helpfully attaches the session cookie.

Defences, in order of strength:

  1. SameSite=Lax or Strict — kills most classic CSRF. Now the browser default for cookies without an explicit attribute.
  2. Anti-CSRF token — a random value in the form, checked server-side. Still required for cross-site flows and for SameSite=None cookies.
  3. Check Origin / Sec-Fetch-Site headers server-side.
  4. Use Authorization: Bearer instead of cookies — headers are not attached automatically, so CSRF doesn't apply (but you then own token storage, and localStorage is XSS-exposed).

60.5 Real-world example

A SaaS moved its API to api.example.com while the app stayed at app.example.com, and set session cookies with Domain=example.com so both would work. Six months later a marketing microsite at promo.example.com — run by an agency, with a stored-XSS hole — leaked every logged-in user's session cookie. The fix: host-only __Host- cookies plus a token exchange for the API, and moving third-party content off the registrable domain entirely.

Try it yourself

curl -sI https://example.com | grep -i set-cookie
# inspect flags in the browser: DevTools > Application > Cookies
# (columns for Secure, HttpOnly, SameSite are exactly the attributes above)

Generated from real-life-example.md · 8 Oct 2026 16:36