Cookie Security: Secure, HttpOnly, and SameSite Flags
Protect session cookies from theft and CSRF attacks by configuring the Secure, HttpOnly, and SameSite flags correctly.
Cookies carry session tokens, authentication state, and user preferences between the browser and your server. If a cookie is stolen or forged, an attacker can impersonate the user without ever knowing their password. Three flags — Secure, HttpOnly, and SameSite — exist specifically to prevent this. Most frameworks support all three, but few enable them by default. Setting them correctly takes minutes and blocks entire categories of attacks. For a broader overview of browser-side protections, see our complete HTTP security headers guide.
The Secure Flag
The Secure flag tells the browser to only send the cookie over HTTPS connections. Without it, a cookie set on https://example.com will also be sent if the user visits http://example.com — over plaintext, visible to anyone on the network.
Set-Cookie: session_id=abc123; Secure
An attacker on the same Wi-Fi network, a compromised router, or any machine along the network path can read plaintext HTTP traffic. If your session cookie travels over HTTP even once, it can be captured. The Secure flag eliminates this entirely. There is no reason to omit it on any site that uses HTTPS, which should be every site.
The HttpOnly Flag
The HttpOnly flag prevents JavaScript from reading the cookie via document.cookie. This is the primary defense against cookie theft through cross-site scripting (XSS).
Set-Cookie: session_id=abc123; HttpOnly
Without HttpOnly, a single XSS vulnerability lets an attacker run document.cookie and exfiltrate every cookie value to a server they control. With HttpOnly set, the cookie is still sent with HTTP requests automatically, but JavaScript cannot access it. The browser enforces this restriction regardless of how the script was injected.
Set HttpOnly on every cookie that does not need to be read by client-side JavaScript. Session tokens, authentication cookies, and CSRF tokens should always be HttpOnly. Cookies that store UI preferences like theme or language may need JavaScript access — those are the exception, not the rule.
The SameSite Flag
The SameSite flag controls whether the browser sends the cookie on cross-site requests. This is the primary browser-level defense against cross-site request forgery (CSRF).
Set-Cookie: session_id=abc123; SameSite=Lax
SameSite Modes
Strict — The cookie is never sent on any cross-site request. If a user clicks a link to your site from an email or another website, the browser will not include the cookie on that initial navigation. This provides the strongest CSRF protection but can cause usability issues — users may appear logged out when arriving from external links.
Use Strict for sensitive operations like banking sessions or admin panels where the inconvenience of re-authenticating is acceptable.
Lax — The cookie is sent on top-level navigations (clicking a link) but not on cross-site subrequests (images, iframes, POST forms from other origins). This is the practical default for most applications. A user clicking a link to your site from a search engine will still appear logged in, but a malicious form on another site cannot submit a POST request with the user's cookies.
Use Lax for general session cookies. It blocks the most common CSRF vectors while preserving normal browsing behavior.
None — The cookie is sent on all cross-site requests with no restrictions. This is required when your cookie genuinely needs to work across origins — for example, cookies used in iframes embedded on third-party sites, or single sign-on flows that span multiple domains. When using SameSite=None, the Secure flag is mandatory. Browsers will reject SameSite=None cookies that are not marked Secure.
Use None only when cross-site cookie delivery is a deliberate requirement, and understand that it provides zero CSRF protection on its own.
Risks of Missing Flags
No Secure flag: Session cookies transmitted over HTTP can be intercepted via network sniffing, ARP spoofing, or man-in-the-middle attacks on public networks. A single plaintext request is enough.
No HttpOnly flag: Any XSS vulnerability — reflected, stored, or DOM-based — becomes a direct path to session hijacking. The attacker's injected script reads document.cookie and sends the token to their server.
No SameSite flag: The browser defaults to Lax in modern versions, but older browsers default to None, sending cookies on every cross-site request. A CSRF attack tricks the user's browser into making authenticated requests to your server from an attacker-controlled page. Without SameSite (or an equivalent CSRF token mechanism), the server cannot distinguish legitimate requests from forged ones.
These risks compound. A cookie with none of these flags can be stolen over the network, exfiltrated via XSS, and used in CSRF attacks simultaneously.
Framework-Specific Configuration
Express.js
app.use(session({
cookie: {
secure: true,
httpOnly: true,
sameSite: 'lax',
maxAge: 3600000
}
}));
Django
In settings.py:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Lax'
Flask
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)
Ruby on Rails
In config/application.rb or an initializer:
Rails.application.config.session_store :cookie_store,
secure: true,
httponly: true,
same_site: :lax
Next.js (API Routes / Middleware)
import { cookies } from 'next/headers';
const cookieStore = await cookies();
cookieStore.set('session_id', token, {
secure: true,
httpOnly: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24
});
Cookie Prefixes: __Host- and __Secure-
Cookie prefixes add a layer of defense enforced by the browser at the naming level. They cannot be bypassed by server misconfiguration.
__Secure- — The browser only accepts this cookie if it was set with the Secure flag over an HTTPS connection. This prevents an attacker from setting a __Secure- prefixed cookie via HTTP.
Set-Cookie: __Secure-session=abc123; Secure; Path=/
__Host- — The strictest prefix. The browser requires the Secure flag, the Path must be /, and there must be no Domain attribute. This means the cookie is locked to the exact origin — no subdomain can set or read it. It prevents subdomain attacks where a compromised sub.example.com overwrites cookies for example.com.
Set-Cookie: __Host-session=abc123; Secure; Path=/
Use __Host- for session cookies whenever possible. It is the strongest cookie isolation mechanism available in browsers today.
Auditing Cookies in Browser DevTools
Open DevTools (F12) and navigate to the Application tab (Chrome/Edge) or the Storage tab (Firefox). Under Cookies, select your domain to see every cookie with its name, value, domain, path, expiration, and flags.
Check for these problems:
- Missing
Secure: The Secure column shows blank or false. Any cookie without this flag is sent over HTTP. - Missing
HttpOnly: The HttpOnly column shows blank or false. These cookies are accessible to JavaScript. - SameSite not set: The SameSite column shows blank. Modern browsers default to
Lax, but you should set it explicitly for clarity and to avoid inconsistent behavior across browser versions. - Overly broad
Domain: A cookie set withDomain=.example.comis sent to every subdomain. Prefer omitting the Domain attribute (which scopes the cookie to the exact host) or use the__Host-prefix.
You can also audit cookies programmatically by running document.cookie in the console. Any cookie that appears in the output is missing the HttpOnly flag. Cookies that should not be readable by JavaScript — session tokens especially — should not show up here.
Run these checks after every deployment. Cookie flags are easy to get right once and easy to regress when session handling code changes. Complement cookie audits with a review of your HTTP security headers to ensure headers like HSTS and CSP reinforce your cookie security posture.
Continue Reading
Form Security and CSRF Protection
Secure web forms against cross-site request forgery, method misuse, and insecure action URLs with framework-specific implementation guides and defense-in-depth strategies.
WAF Detection and Fingerprinting: How CyberShield Identifies Web Application Firewalls
Understanding what web application firewall protects a target is essential context for any security assessment. Learn how CyberShield passively fingerprints 15+ WAF vendors through header analysis, error patterns, and behavioral signatures.
Web Vulnerability Assessment: What Passive Analysis Reveals Without Firing a Single Exploit
Passive web analysis uncovers OWASP-relevant vulnerabilities -- information leaks, form weaknesses, exposed files, and redirect flaws -- without touching a single exploit.