1. Knowledge base
  2. Findings
Check: web.cookies.name_prefix

Fixing session cookies that lack a __Host- or __Secure- prefix

Browsers apply special rules to cookies whose names begin with __Host- or __Secure-. A session cookie with a plain name like session or sid gets none of that protection. SetEnforce flags the gap when a response sets a cookie that looks like a session identifier and the name does not use one of these prefixes. This article explains what the finding means, how the scanner reaches it, and how to fix it.

What the prefixes do

The prefixes are defined in section 4.1.3 of the HTTP State Management Mechanism draft (rfc6265bis). Compliant browsers enforce them when a Set-Cookie header arrives.

  • __Secure-: the browser accepts the cookie only if it is set with the Secure attribute from a secure (HTTPS) origin.
  • __Host-: it needs everything __Secure- needs, and the cookie must also have Path=/ and no Domain attribute. That binds the cookie to the exact host that set it, so subdomains cannot share it.

If a response violates these rules, the browser discards the cookie. A misconfiguration fails visibly instead of silently weakening the session.

Why a plain name is a weakness

Cookie attributes protect a cookie after it is stored. They say nothing about who can write it. Browsers do not authenticate the origin of a cookie write, so a sibling subdomain, or an attacker on the network, can often set a cookie that the main application will read.

That opens three attacks:

  1. Cookie tossing. An attacker who controls any subdomain (a forgotten staging host, a user-content site, a subdomain takeover) sets session=<attacker value>; Domain=example.com. The browser sends both the legitimate and the planted cookie. Depending on ordering, the application may accept the attacker's value, which enables session fixation or login CSRF.
  2. Downgrade overwrite. If a user briefly loads an http:// URL, an active attacker on the network path can inject a Set-Cookie response that overwrites the stored session cookie. A Secure attribute alone does not stop this in all browsers, because the older rules let insecure origins overwrite Secure cookies. The prefix rules close that hole.
  3. Scope creep. A session cookie accidentally scoped with Domain=example.com is sent to every subdomain, including ones that run less trusted code.

The prefix turns these conventions into guarantees the browser enforces. A __Host- cookie cannot be set from a subdomain or over cleartext, so the application knows any cookie with that name came from itself over HTTPS.

Why it is rated low

SetEnforce rates this finding low because it is a defense-in-depth gap, not a direct exposure. A session cookie without a prefix cannot be read or stolen because of that alone. Exploiting the missing prefix usually needs a prior foothold: control of a sibling subdomain, or an active network position combined with a user visiting a cleartext URL. If the cookie is also missing Secure, HttpOnly or SameSite, those separate findings carry more weight.

The cost is real but indirect. A successful tossing or fixation attack means account takeover, which puts you in breach-notification territory. Auditors working from OWASP session-management guidance and similar control frameworks expect prefixes on the session cookies of any modern application, so a missing prefix is an easy item for them to cite.

How the scanner detects and rates it

During the web checks, SetEnforce requests the domain's main page and follows redirects to the final HTTPS response. It collects every Set-Cookie header from the chain and classifies each cookie.

A cookie counts as session-like if its name matches a common session pattern (session, sid, PHPSESSID, JSESSIONID, connect.sid, auth, token and similar), or if it is HttpOnly and has no explicit lifetime. For each session-like cookie, the scanner checks whether the name starts with __Host- or __Secure-, and whether the attributes satisfy that prefix.

The finding is raised at severity low when at least one session-like cookie has no prefix. The evidence lists the cookie name, the attributes observed (Secure, HttpOnly, SameSite, Domain, Path), and the URL that set it. A cookie that carries a prefix but violates its rules, for example __Host-sid with a Domain attribute, is reported too, although a real browser would reject it. Non-session cookies such as analytics or language preferences are ignored.

The check passes when every session-like cookie observed uses __Host- or __Secure- and meets that prefix's requirements. It reports not tested if the site sets no cookies on unauthenticated pages, because the scanner never logs in.

How to fix it

1. Pick the right prefix

Use __Host- for a session cookie the application sets for its own host only, which covers almost every case. Use __Secure- only when the cookie must be shared across subdomains, for example single sign-on across app.example.com and api.example.com. Be aware that __Secure- leaves the cookie-tossing path from subdomains open.

A valid __Host- cookie looks like this:

Set-Cookie: __Host-sid=3f9c1e0a7b; Path=/; Secure; HttpOnly; SameSite=Lax

Do not add Domain, and do not use any Path other than /.

Framework examples:

# Flask
app.config.update(
    SESSION_COOKIE_NAME="__Host-sid",
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
    SESSION_COOKIE_PATH="/",
    SESSION_COOKIE_DOMAIN=None,
)
// Express (express-session)
app.use(session({
  name: '__Host-sid',
  secret: process.env.SESSION_SECRET,
  cookie: { secure: true, httpOnly: true, sameSite: 'lax', path: '/' },
}));
// php.ini
session.name = "__Host-PHPSESSID"
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = "Lax"

If TLS terminates at a proxy, make sure the application knows the original scheme (for example through X-Forwarded-Proto). Otherwise frameworks may omit Secure, and the browser will drop the prefixed cookie.

3. Handle the rollout

Renaming a session cookie logs everyone out, because existing browsers hold the old name. Plan for that:

  • Deploy during a low-traffic window, or accept both names for a short transition and issue only the new one.
  • Expire the old cookie explicitly with Set-Cookie: sid=; Max-Age=0; Path=/.
  • Update anything that references the name: load balancer stickiness rules, WAF rules, log parsers, and integration tests.

Confirming the fix on re-scan

Start by checking the header yourself:

curl -sI https://example.com/ | grep -i '^set-cookie'

Every session cookie should begin with __Host- or __Secure- and show the attributes above. Next, open browser developer tools, load the site, and confirm the cookie is present in storage. If it is missing, the browser rejected it. That usually means Secure was dropped behind a proxy or a Domain attribute is still being set.

Then trigger a new scan from the domain page. The finding should clear, and the cookie evidence should show the prefixed name with Secure, HttpOnly and SameSite present. If it persists, compare the evidence URL with the response you tested. A different endpoint, such as a login redirect or a CDN-served page, may set its own cookie.

Key takeaways

  • The __Host- and __Secure- prefixes make the browser enforce Secure (and, for __Host-, host-only scope with Path=/), which blocks cookie tossing and downgrade overwrites.
  • A missing prefix is a low-severity hardening gap, not a direct leak, but it makes session fixation and takeover easier for an attacker with a foothold.
  • Prefer __Host- for session cookies. Use __Secure- only when subdomains must share the cookie.
  • Renaming logs users out, so plan the transition and clean up the old cookie.
  • Verify with curl and browser tools, then re-scan to confirm the finding clears.

Check whether your own domain gets this right. Scanning is free and needs no account.

Scan your domain

All articles