RedScore.ai

Fixes

Cookie & Privacy Hygiene · Updated 2026-05-02

HttpOnly Audit

Set HttpOnly on every session cookie. JavaScript cannot read HttpOnly cookies, so XSS cannot steal auth tokens.

The HttpOnly flag on a cookie tells the browser to keep that cookie out of JavaScript. Code running on the page (including code injected via XSS) cannot read the cookie via document.cookie or any other JS API. This is the single most important defense against XSS-driven session hijacking: even if an attacker injects a script into your page, they cannot exfiltrate the session token.

RedScore parses every cookie set in first-response Set-Cookie headers across primary hosts and audits the HttpOnly flag. Like the Secure flag check, session cookies missing HttpOnly are scored harder than other cookies because they directly carry authentication.

How the check works

Per primary host, the check tallies cookies missing HttpOnly and splits them by whether the cookie name looks like a session cookie (heuristic match against patterns like "session", "sessid", "auth", "jwt", "token", framework-specific names like "connect.sid"). The host score:

  • 1.0 (full credit): every cookie has HttpOnly.
  • 0.5 (partial): no session cookies missing HttpOnly, but at least one non-session cookie missing it. Reason: COOKIE_HTTPONLY_NONSESSION_WARN.
  • 0.0 (zero): at least one session cookie missing HttpOnly. Reason: COOKIE_HTTPONLY_SESSION_MISSING.

Per-host scores are weighted (apex highest) and averaged across all primary hosts to produce the category score (33 pts possible).

How the verdict maps to evidence

  • Pass: all cookies on every primary host have HttpOnly.
  • Warn: at least one non-session cookie missing HttpOnly on at least one host.
  • Fail: at least one session cookie missing HttpOnly on at least one host.

Fix: set HttpOnly on every session cookie

If a cookie carries authentication, session state, or any token JavaScript does not need to read, set HttpOnly. The fix is per-framework, usually a one-line setting alongside Secure and SameSite.

Per-framework snippets

Express / Node.js (express-session)

import session from "express-session";

app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: {
    secure: true,
    httpOnly: true,      // HttpOnly flag (default in express-session)
    sameSite: "lax",
    maxAge: 1000 * 60 * 60 * 8,
  },
}));

Django

# settings.py
SESSION_COOKIE_HTTPONLY = True   # default in modern Django; verify it is on
CSRF_COOKIE_HTTPONLY = False     # CSRF cookie often needs to be readable by JS
                                 # (Django default; only enable if your CSRF flow does not require JS access)

Rails

# config/environments/production.rb
Rails.application.config.session_store :cookie_store,
  key: "_yourapp_session",
  secure: true,
  httponly: true,        # default in Rails; verify
  same_site: :lax

ASP.NET Core

// Program.cs
services.Configure<CookiePolicyOptions>(options => {
    options.HttpOnly = HttpOnlyPolicy.Always;
});

services.AddSession(options => {
    options.Cookie.HttpOnly = true;
});

Spring Boot

# application.properties
server.servlet.session.cookie.http-only=true

Laravel

// config/session.php
'http_only' => true,

Plain Set-Cookie header

Set-Cookie: session_id=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

When you might NOT want HttpOnly

There are a small number of legitimate reasons to leave a cookie readable by JS:

  • Double-submit CSRF tokens. Some CSRF defenses store the token in a cookie that JavaScript reads and echoes back in a header. The token cookie is non-session and not authentication-bearing; it can be readable. The session cookie itself stays HttpOnly.
  • Legacy SPA token storage. Some older SPAs persist auth tokens in a JS-readable cookie. Modern best practice is to use a pure HttpOnly session cookie (browser handles auth automatically) or an Authorization: Bearer header (no cookie at all).
  • User preferences, theme, language. Non-sensitive preference cookies that need to render before JS hydration can read them. Low-risk; HttpOnly is preferred where feasible but the warn cost is small.

The score allows the warn-tier outcome for non-session cookies. The fail-tier hits only when a SESSION cookie is missing HttpOnly, and there is rarely a good reason to leave a session cookie readable by JS.

Verify the fix

  • curl -sI -c /dev/null https://yourdomain.tld | grep -iE 'set-cookie' shows the Set-Cookie headers. Confirm every session-related cookie has HttpOnly.
  • Open the page in a browser, open DevTools → Application → Cookies → yourdomain.tld. Check the "HttpOnly" column.
  • Open DevTools console and try: document.cookie. HttpOnly cookies do not appear in this output, even though the browser still sends them with each request.
  • Re-run the RedScore lookup. Pass requires HttpOnly on every cookie across every primary host.

Common pitfalls

  • Reading the session token from JavaScript by design. If your auth flow currently reads document.cookie to grab a session token, redesign before adding HttpOnly. The fix is to either let the browser handle auth via cookies automatically (no JS read needed), or move auth to an Authorization: Bearer header that lives in JS memory (not a cookie at all).
  • Manually-coded Set-Cookie missing HttpOnly. Direct Set-Cookie writes outside the framework's session middleware skip the default. Grep your codebase for direct cookie sets and add the attribute.
  • Third-party widgets setting their own cookies. Analytics, ads, embeds set cookies via JavaScript. Those cookies cannot have HttpOnly because they are JS-set in the first place. They show up in this audit. Either configure the third-party server to set them via Set-Cookie (with HttpOnly), or accept the warn.
  • HttpOnly does not prevent XSS exploitation entirely. An attacker with XSS can still issue authenticated requests through the page (the cookie is sent automatically). HttpOnly stops EXFILTRATION of the token, not abuse of the active session. Pair with strong CSP, SameSite, and short session lifetimes.
  • HttpOnly does not protect tokens in URL paths or query strings. If your app puts session tokens in URLs (it should not; see Referrer-Policy), HttpOnly does not help.
  • JS frameworks reading CSRF tokens. If your CSRF token is in a cookie that JS reads, that cookie cannot have HttpOnly. The session cookie still should. Use separate cookies for separate concerns.

What to do next

See how these recommendations apply to your site's current scan results.

Scan domain