Guide
Secure, HttpOnly and the __Host- and __Secure- Prefixes
What the Secure and HttpOnly flags protect against, and how the __Host- and __Secure- cookie name prefixes make browsers enforce stricter rules for you.
Key takeaways
- Secure keeps a cookie off plain HTTP (localhost is an exception); HttpOnly hides it from page scripts.
- HttpOnly limits cookie theft through XSS but does not stop injected script from making authenticated requests.
- __Secure- requires the Secure attribute and a secure page; __Host- also requires Path=/ and forbids a Domain attribute.
- A cookie that breaks a prefix rule is dropped without a visible error, so check the developer tools.
- Prefixes complement Secure, HttpOnly and SameSite; they do not replace them.
Two flags that every session cookie should have
Among all cookie attributes, two are about protection rather than scope. Secure protects the cookie while it travels. HttpOnly protects the cookie once it is sitting in the browser. Neither flag takes a value; the bare word is enough. Together they remove two of the most common ways that a session cookie leaks, and they cost almost nothing to add.
What does the Secure flag do?
A cookie marked Secure is sent by the browser only over an encrypted connection, which in practice means HTTPS. If a visitor's browser makes a plain HTTP request to the same host, the cookie is left out. This matters on shared networks, where anyone able to observe plain HTTP traffic could otherwise read a session identifier and reuse it. Current browsers also generally refuse to let a plain HTTP response create or overwrite a Secure cookie, which closes a related trick where an attacker on the network injects a cookie of their own.
Secure only helps if your site really uses HTTPS everywhere. Pair it with a redirect from HTTP to HTTPS and, where appropriate, the Strict-Transport-Security response header so that browsers do not even attempt the plain request. Note that browsers usually treat localhost as a secure context for development, so a Secure cookie may work there even without a certificate. Behaviour differs between browsers, so test rather than assume.
What does the HttpOnly flag do?
A cookie marked HttpOnly is hidden from scripts. The document.cookie property will not list it, and it cannot be read or written by JavaScript running in the page. The browser still attaches it to requests automatically, which is what you want for a session identifier. The value of this flag shows up when a page suffers a cross-site scripting flaw. An injected script cannot simply copy the session cookie and send it to a remote server.
It is worth being precise about the limits. HttpOnly does not stop an injected script from making requests as the logged-in user, because the browser will still attach the cookie to those requests. It only prevents theft of the raw cookie value. It is a layer of defence, not a cure for XSS. The real fix is to stop script injection by encoding output, using a content security policy and validating input.
A quick comparison
- Secure answers: over which kind of connection may this cookie be sent?
- HttpOnly answers: may page scripts see this cookie?
- Neither flag limits which site may trigger a request. That is the job of SameSite, covered in another guide.
Cookie prefix rules at a glance
| Requirement | No prefix | __Secure- | __Host- |
|---|---|---|---|
| Secure attribute | Optional | Required | Required |
| Set from a secure page (HTTPS) | Not required | Required | Required |
| Domain attribute | Allowed | Allowed | Not allowed |
| Path=/ | Optional | Optional | Required |
What problem do cookie prefixes solve?
Attributes are set by the server, but the server cannot easily prove later that a cookie it receives was created with the attributes it intended. A request carries only the name and value. Suppose an attacker controls a sibling subdomain, or can inject headers on an insecure connection. They might plant a cookie that your application then trusts. Cookie name prefixes exist so that the browser itself enforces a minimum set of rules before it accepts a cookie with a special name.
What does the __Secure- prefix require?
If a cookie name begins with __Secure-, current browsers will accept it only when the Set-Cookie header includes the Secure attribute and the response came over a secure connection. The check happens at storage time. So Set-Cookie: __Secure-sid=abc123; Secure; Path=/ is accepted over HTTPS, and the same header without Secure is rejected. The prefix is case sensitive in the way that matters, so the capitalisation and double underscore must be exactly as specified.
What does the __Host- prefix require?
The stricter prefix is __Host-. A cookie with this name is accepted only when all of the following hold: it has the Secure attribute, it was set from a secure connection, it has Path=/, and it has no Domain attribute at all. The result is a cookie locked to the exact host that set it. A sibling subdomain cannot overwrite it, because host-only cookies are keyed to the host, and a Domain-scoped cookie from elsewhere cannot take over the same name under this prefix.
A valid example is Set-Cookie: __Host-sid=abc123; Secure; HttpOnly; Path=/; SameSite=Lax. For login sessions, this is often the best form available, and it is a good default for new projects whenever you do not need to share the cookie across subdomains.
Using prefixes in practice
- Pick __Host- when the cookie belongs to a single host and can use Path=/.
- Pick __Secure- when you must use a Domain attribute to share across subdomains but still want the Secure guarantee.
- Remember that the browser enforces the prefix rules on setting, so a misconfigured header fails quietly. Check the devtools cookie panel if your cookie never appears.
- Keep HttpOnly and SameSite on session cookies as well. Prefixes complement the flags rather than replace them.
Things to remember
Prefixes are a browser feature, so the server must still validate sessions properly. Older or unusual clients may not enforce the prefix, which is another reason to treat the server as the final authority. Also, renaming a cookie to add a prefix changes its identity: existing users will arrive with the old name, so plan a migration in which you accept both names for a short time, then retire the old one. Done carefully, Secure, HttpOnly and a suitable prefix give a strong baseline for any authentication cookie.
Sources and further reading
- MDN: Set-Cookie header: each attribute (Domain, Path, Expires, Max-Age, Secure, HttpOnly, SameSite, Partitioned) and the cookie prefix rules.
- MDN: Using HTTP cookies: session and permanent cookies, deleting a cookie, regenerating session cookies at login, and why cookies suit storage poorly compared with Web Storage.
Frequently asked questions
Does HttpOnly stop cross-site scripting?
No. It stops a script from reading the cookie value, but an injected script can still make requests that carry the cookie. You still need output encoding and a content security policy.
Why was my __Host- cookie rejected?
The browser requires Secure, a secure connection, Path=/, and no Domain attribute. If any one is missing, the cookie is dropped without a visible error, so check the header carefully.
Do I need Secure on localhost?
Browsers generally treat localhost as a secure context, so Secure cookies often work there. Behaviour can differ by browser, so verify in the ones you target.
What is the difference between __Secure- and __Host-?
Both require Secure. The __Host- prefix additionally requires Path=/ and forbids a Domain attribute, which locks the cookie to a single host.