Cookie Security: Every Flag Developers Must Set in 2025
Cookies are the backbone of web authentication. Every time a user logs in, their session token is stored in a cookie. If that cookie is not properly secured, an attacker can steal it and impersonate the user. This is not a theoretical risk — session hijacking through insecure cookies remains one of the most common web attacks.
The good news is that securing cookies is straightforward. A handful of flags and attributes, properly configured, can eliminate entire categories of attacks. This guide covers every flag you need to know, explains what each one prevents, and shows you how to set them in the most popular web frameworks.
The Cost of Insecure Cookies
Before diving into the technical details, consider what an insecure session cookie enables:
- Session hijacking — An attacker steals the cookie and takes over the user account
- Cross-site request forgery (CSRF) — An attacker tricks the user's browser into making authenticated requests
- Cross-site scripting (XSS) data theft — Malicious JavaScript reads cookie values and sends them to the attacker
- Man-in-the-middle interception — Cookie transmitted over HTTP can be read by anyone on the network
Each of these attacks is preventable with the right cookie configuration.
The Essential Cookie Flags
Secure
The Secure flag ensures the cookie is only sent over HTTPS connections. Without it, the cookie will be transmitted in plain text over HTTP, where anyone on the same network can intercept it.
Set-Cookie: session=abc123; SecureWhen to use it: Always. Every cookie that contains sensitive data (especially session tokens) must have the Secure flag. There is no valid reason to send session cookies over HTTP in 2025.
Common mistake: Setting the Secure flag in production but not in local development, then forgetting to re-enable it. Use environment-based configuration to handle this automatically.
HttpOnly
The HttpOnly flag prevents JavaScript from accessing the cookie via document.cookie. This is your primary defense against XSS-based session theft.
Set-Cookie: session=abc123; HttpOnlyWhy it matters: Even if an attacker finds an XSS vulnerability in your application, they cannot steal HttpOnly cookies. The cookie is still sent with requests, but it is invisible to client-side scripts.
When to skip it: Only for cookies that JavaScript legitimately needs to read, like theme preferences or UI state. Never skip HttpOnly for session tokens or authentication cookies.
SameSite
The SameSite attribute controls whether the cookie is sent with cross-site requests. It is your primary defense against CSRF attacks.
Three values:
- Strict — Cookie is never sent with cross-site requests. Most secure, but can break legitimate flows (e.g., clicking a link from an email to your site will not include the cookie).
- Lax — Cookie is sent with top-level navigation (clicking links) but not with embedded requests (forms, images, iframes). This is the recommended default.
- None — Cookie is sent with all cross-site requests. Requires the Secure flag. Only use this for legitimate cross-site scenarios like embedded widgets or OAuth flows.
Set-Cookie: session=abc123; SameSite=LaxDefault behavior: Modern browsers default to SameSite=Lax if no attribute is set. However, explicitly setting it is best practice for clarity and compatibility.
Domain and Path
The Domain attribute controls which domains receive the cookie. The Path attribute restricts the cookie to specific URL paths.
Set-Cookie: session=abc123; Domain=example.com; Path=/appBest practice: Set the Domain to your exact domain (not a wildcard that includes all subdomains) and the Path to the most specific path possible. This limits cookie exposure.
Max-Age and Expires
These attributes control how long the cookie persists. A session cookie (no Max-Age or Expires) is deleted when the browser closes. A persistent cookie lives for the specified duration.
Set-Cookie: session=abc123; Max-Age=3600Security guideline: Session tokens should have a short lifetime (1-4 hours for active sessions, 30 minutes for sensitive operations). Long-lived cookies increase the window for session hijacking.
Framework Examples
Express.js (Node.js)
app.use(session({
cookie: {
secure: true,
httpOnly: true,
sameSite: 'lax',
maxAge: 3600000,
domain: 'example.com',
path: '/'
}
}));Django (Python)
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_AGE = 3600
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = TrueLaravel (PHP)
// config/session.php
'secure' => true,
'http_only' => true,
'same_site' => 'lax',
'lifetime' => 60, // minutesNext.js API Routes
res.setHeader('Set-Cookie', [
`session=${token}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600`
]);Cookie Security Checklist
Use this checklist for every cookie your application sets:
- 1Session cookies have the Secure flag
- 2Session cookies have the HttpOnly flag
- 3SameSite is set to Lax or Strict
- 4Cookie domain is as specific as possible
- 5Session lifetime is reasonable (not months)
- 6No sensitive data stored directly in cookie values
- 7Cookie names do not reveal technology stack (avoid PHPSESSID, JSESSIONID)
- 8Third-party cookies are audited and minimized
- 9Cookie consent is implemented for tracking cookies
- 10Cookies are tested with and without HTTPS
Testing Your Cookie Security
You can check your cookie configuration in several ways:
- Browser DevTools: Open Application > Cookies to see all flags for each cookie
- ZeriFlow scan: Automatically checks all cookies for missing security flags and reports specific recommendations
- curl command: Use
curl -I https://yoursite.comand inspect the Set-Cookie headers
Beyond Flags: Cookie Architecture
Proper cookie flags are necessary but not sufficient. A complete cookie security strategy also includes:
- Token rotation — Issue a new session token after login and privilege escalation
- Token binding — Tie the session to the user's IP or user-agent to detect theft
- Logout invalidation — Server-side session destruction, not just cookie deletion
- Cookie prefixes — Use __Secure- and __Host- prefixes for additional browser-enforced security
Getting cookie security right is one of the highest-impact, lowest-effort improvements you can make. Set the flags, test them, and automate the verification with regular ZeriFlow scans.