# 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.

Source: https://howhttpworks.com/cookies/cookie-prefixes
Last reviewed: 2026-10-04

> **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](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-4.1.3) (still an Internet-Draft; it was in the RFC Editor queue when this page was last reviewed):

| Prefix | Secure attribute | Set from HTTPS | Domain attribute | Path attribute |
| --- | --- | --- | --- | --- |
| `__Secure-` | Required | Required | Allowed | Any |
| `__Host-` | Required | Required | Forbidden | Must be `/` |

```http
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](https://howhttpworks.com/cookies/secure) attribute alone leaves the subdomain case open.

## Setting a __Host- cookie in common stacks

Express with `express-session`:

```javascript
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:

```bash
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](https://howhttpworks.com/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.
