HttpOnly cookies: keeping session tokens away from XSS
The flag that defangs a stolen page
Cross-site scripting (XSS) means an attacker got their own JavaScript to run on your page. Once that happens, what do they do with it? Overwhelmingly, the first move is to grab the session cookie and send it somewhere — because a session cookie is a logged-in user in a single string. The HttpOnly flag exists to make that one move impossible.
What HttpOnly actually does
When a cookie is set with HttpOnly, the browser keeps it out of reach of scripts. It still travels on every request to your server as normal, so logins keep working exactly as before. But document.cookie in JavaScript simply will not show it. A script running on your page cannot read the value, cannot copy it, cannot post it to an attacker-controlled server.
A cookie header with the protections looks like this:
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax
Three small attributes, each closing a different door.
It limits damage, it does not prevent XSS
Let's be precise, because this is where the flag is oversold. HttpOnly does not stop cross-site scripting. If an attacker can run script on your page, they can still do plenty within that page — submit forms as the user, read on-screen data, drive the UI. What HttpOnly removes is the cleanest, most portable prize: a copy of the session token they can use from anywhere, long after the visitor leaves. That is a meaningful reduction in blast radius, which is why it is standard hygiene.
The cookies that need it
- Session and auth cookies — always. These are the high-value targets. There is essentially no reason for client JavaScript to read them.
- CSRF tokens — usually not. Some patterns need JavaScript to read the CSRF token to echo it back in a header, so those are deliberately not HttpOnly. That is fine; they are not the session.
- Analytics and UI cookies — your call. No secret, lower stakes, but defaulting to HttpOnly where scripts do not need the value costs nothing.
The rule of thumb is simple: if no part of your frontend reads the cookie, there is no reason to leave it readable. The fewer cookies a stray script can touch, the less an injection can do.
Why it is so often missing
Many frameworks set HttpOnly on their session cookie by default — but plenty of hand-rolled auth, quick prototypes, and AI-generated boilerplate set a cookie with no flags at all. The AI writes the code you asked for, not the security you didn't mention. The result is a session cookie that any injected script can read, which turns a small XSS bug into a full account takeover.
Check your cookies
You cannot see cookie flags by looking at your site; they live in the response headers. An automated scan reads the actual Set-Cookie lines your server sends and flags any session cookie missing HttpOnly, Secure or SameSite. Scan your site and see how your cookies are configured.