How HTTP Works

Cookie attribute

< Set-Cookie: __Host-id=a3fWa; Secure; Path=/

Cookie Prefixes: __Host- and __Secure- Rules

Cookie prefixes make the browser enforce attributes. The exact __Host- and __Secure- rules from RFC 6265bis, how to set them, and why a cookie vanishes.

Reviewed 3 min readintermediate3 sourcesTry itMarkdown
Without a prefix
The browser applies no extra checks to the name
Accepts
__Secure- (needs Secure) or __Host- (Secure, Path=/, no Domain)
Sent back to server
Yes: the prefix is part of the cookie name
Spec
RFC 6265bis §4.1.3
On this page

TL;DR: A cookie whose name starts with __Host- is only accepted if it has Secure, Path=/ and no Domain, and was set over HTTPS. __Secure- only demands Secure over HTTPS. The browser enforces this, so a subdomain or a network attacker cannot overwrite your session cookie.

The rules

From RFC 6265bis section 4.1.3 (still an Internet-Draft; it was in the RFC Editor queue when this page was last reviewed):

PrefixSecure attributeSet from HTTPSDomain attributePath attribute
__Secure-RequiredRequiredAllowedAny
__Host-RequiredRequiredForbiddenMust be /
Set-Cookie: __Secure-theme=dark; Secure; Domain=example.com; SameSite=Lax
Set-Cookie: __Host-sid=a3fWa9; Secure; Path=/; HttpOnly; SameSite=Lax

If a Set-Cookie header with a prefixed name violates its rules, the browser discards the cookie. There is no error in the page and no Set-Cookie echo in document.cookie; the symptom is “the cookie never appears”. Server-side, the draft says a name starting with the case-sensitive string __Secure- implies the cookie was set with Secure. User agents must match the prefix case-insensitively, which prevents a mis-capitalized __host- name from dodging the checks while a server that folds case still reads it.

What problem prefixes solve

Cookies are scoped by host, not origin, and a cookie from evil.example.com with Domain=example.com is sent to app.example.com. If it has the same name as your session cookie, the browser may send both, and many frameworks read the first one. This is cookie tossing, and it feeds session fixation and CSRF-token overwrite. An HTTP-only network attacker can do the same by injecting a cookie into a plain HTTP response for your domain.

  • __Host- closes both holes. Without Domain the cookie is host-only, so a sibling subdomain cannot set one that collides, and Secure stops the HTTP injection. Path=/ stops a different path on the same host from planting one.
  • __Secure- only closes the HTTP injection. It is for cookies that must span subdomains.

Prefixes are the right default for session and CSRF cookies. The Secure attribute alone leaves the subdomain case open.

Express with express-session:

app.set('trust proxy', 1) // so req.secure is true behind a TLS-terminating proxy
app.use(session({
  name: '__Host-sid',
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: true, httpOnly: true, sameSite: 'lax', path: '/' } // no domain
}))

Laravel reads these from config/session.php, with environment overrides:

SESSION_COOKIE=__Host-laravel_session
SESSION_SECURE_COOKIE=true
SESSION_PATH=/
SESSION_DOMAIN=null

SESSION_DOMAIN=null is what keeps Domain out of the header. If another layer, such as a CDN rule or a legacy middleware, appends Domain=, the cookie vanishes. From JavaScript the same restrictions apply: document.cookie = "__Host-flag=1; Secure; Path=/" works on HTTPS pages and is rejected on http://.

Debug checklist

  1. Run curl -si https://example.com/login | grep -i set-cookie and look for Domain= and Path= on the __Host- line.
  2. Behind a proxy, confirm the app sees HTTPS (X-Forwarded-Proto); a framework that only adds Secure for HTTPS requests will omit it, and the browser will drop the cookie.
  3. Make sure every environment, including localhost, serves the cookie over HTTPS. Chrome and Firefox treat http://localhost as secure; Safari has historically not.
  4. Change the cookie name only with a migration plan: users with the old unprefixed cookie are logged out when you rename it.

Newer prefixes

MDN also documents __Http- (requires Secure and HttpOnly) and __Host-Http- (the __Host- rules plus HttpOnly). Its compatibility data lists them as supported from Chrome 140 and Firefox 143, and not in Safari. Use them as an addition where you can, not as a replacement for __Host-.

With Partitioned cookies

Partitioned cookies are recommended to use the __Host- prefix, so a third-party embed’s state is bound to its own host and partition.

Frequently asked questions

What does the __Host- cookie prefix require?

All three of these, or the browser rejects the cookie: a Secure attribute, a Path attribute of exactly /, and no Domain attribute. The cookie also has to be set from a secure (HTTPS) origin. The result is a cookie bound to one exact host that no sibling subdomain can overwrite.

What does the __Secure- prefix require?

Only that the cookie is set with the Secure attribute from a secure origin. It permits Domain, so it is the right choice for a cookie that must be shared across subdomains, though it gives weaker guarantees than __Host-.

Why is my __Host- cookie missing in the browser?

The browser silently drops a __Host- cookie that breaks any rule. The usual culprits are a Domain attribute added by a framework default, Path set to something other than /, a missing Secure flag because TLS terminates at a proxy, or the response arriving over plain HTTP. Check the Set-Cookie line in the Network panel.

Are cookie prefixes case-sensitive?

Servers should treat the prefix as case-sensitive, but the draft requires user agents to match it case-insensitively, so __host-id gets the same restrictions as __Host-id in a conforming browser. Use the exact spelling anyway.

Do prefixes work in all browsers?

Yes for __Secure- and __Host-. MDN compatibility data lists Chrome 49, Firefox 50 and Safari 13 as the first versions with support, so you can rely on them for any current audience. The newer __Http- and __Host-Http- prefixes are not supported in Safari.

Sources

  1. MDN Web Docs: Set-Cookie, cookie prefixesdeveloper.mozilla.org
  2. RFC 6265bis (draft) section 4.1.3: Cookie Name Prefixesdatatracker.ietf.org
  3. Laravel: HTTP Session configurationlaravel.com

Keep going

Browse /search