Secure and Host Cookie Prefixes
Enforce HTTPS-only and domain-restricted cookies through __Secure- and __Host- naming conventions
Introduction to Cookie Prefixes: Strengthening Cookie Security
Imagine you're a developer who's carefully configured your authentication cookies with the Secure flag, ensuring they only travel over HTTPS. You've added HttpOnly to prevent JavaScript theft, and you've set up your domain carefully. Everything seems locked down. Then, during a security audit, you discover that an attacker using an insecure subdomain could inject their own cookie that overrides yoursβand your browser would accept it without question. This nightmare scenario is precisely why cookie prefixes were invented, and understanding them could be the difference between a secure application and a devastating breach. Master these concepts with our free flashcards as we explore how modern browsers strengthen cookie security beyond what traditional attributes can achieve.
For years, web developers relied on cookie attributes like Secure, HttpOnly, Domain, and Path to protect sensitive session data. These attributes work wellβwhen used correctlyβbut they share a critical flaw: they can't prevent cookie injection attacks. Here's the problem: when a browser receives a cookie, it doesn't verify that the attributes were set by the legitimate server. A malicious subdomain, an HTTP connection on a misconfigured site, or even a compromised network could inject cookies that appear legitimate to the browser.
π― Key Principle: Traditional cookie attributes are defensive recommendations, not cryptographic guarantees. The browser enforces them after accepting the cookie, but it can't verify the cookie's origin or the integrity of its initial attributes.
Consider this real-world vulnerability: You set a session cookie sessionid=abc123 with the Secure flag from https://bank.example.com. An attacker controls http://malicious.example.com (notice the HTTP, not HTTPS). Because cookies are scoped by domain, that attacker can inject their own sessionid=hacked456 cookie without the Secure flag. Depending on cookie precedence rules and how your server validates sessions, this could lead to session fixation attacks or authentication bypasses.
π‘ Real-World Example: In 2015, researchers demonstrated cookie injection attacks against major websites by exploiting mixed-content scenarios and subdomain takeovers. Attackers would set cookies from insecure contexts that overrode secure cookies, leading to session hijacking despite properly configured Secure flags.
The Cookie Prefix Solution
Enter cookie prefixesβa elegant solution standardized in RFC 6265bis. Rather than adding more attributes that could be ignored or spoofed, browser vendors took a different approach: they made the cookie name itself carry security guarantees. By requiring cookies to start with specific prefixes (__Secure- and __Host-), browsers enforce strict security requirements before accepting the cookie at all.
Here's the breakthrough: When a browser sees a cookie named __Secure-sessionid, it immediately checks whether the cookie meets specific security criteria. If it doesn't, the browser rejects it entirelyβno exceptions, no precedence games, no injection possible.
Legitimate server (HTTPS): Set-Cookie: __Secure-session=abc123; Secure
β
Accepted (meets requirements)
Attacker (HTTP subdomain): Set-Cookie: __Secure-session=hacked456
β Rejected (not sent over HTTPS)
The beauty of this mechanism is that it's self-enforcing. The security guarantee is baked into the name that both client and server must agree upon. An attacker can't remove the prefix without changing the cookie name entirely, which would make it a different cookie that your application wouldn't recognize.
Two Levels of Protection
The specification defines two prefixes, each offering escalating levels of protection:
π __Secure- ensures the cookie can only be set over HTTPS connections and will only be sent over HTTPS. This prevents any insecure context from injecting or intercepting the cookie.
π __Host- provides even stronger guarantees: it requires HTTPS, plus it locks the cookie to the exact host (no Domain attribute allowed) and sets Path=/. This prevents subdomain attacks and path-based injection entirely.
π‘ Mental Model: Think of __Secure- as a "HTTPS-only" padlock and __Host- as a "HTTPS-only + address-specific" vault. The first protects against insecure transport; the second protects against both insecure transport and domain/path manipulation.
Integration with the Modern Cookie Security Model
Cookie prefixes don't replace traditional attributesβthey complement them. A complete modern cookie security strategy layers multiple defenses:
| π‘οΈ Defense Layer | π― Protects Against | π§ Mechanism |
|---|---|---|
| π __Host- prefix | Subdomain injection, path manipulation | Browser enforces origin restrictions |
| π __Secure- prefix | HTTP injection/interception | Browser requires HTTPS |
| π HttpOnly attribute | XSS cookie theft | Blocks JavaScript access |
| π SameSite attribute | CSRF attacks | Controls cross-site sending |
| π Secure attribute | Network sniffing | HTTPS-only transmission |
π€ Did you know? Cookie prefixes were first implemented in Chrome 49 (2016) and Firefox 50 (2016), making them one of the newer cookie security features. Despite this, they're widely supported and backward-compatibleβolder browsers simply treat the prefix as part of the cookie name.
β οΈ Common Mistake: Developers sometimes think that adding __Secure- or __Host- to a cookie name is purely cosmetic, just a naming convention. This is wrong thinking. β Wrong thinking: "The prefix is just documentation for developers." β
Correct thinking: "The prefix triggers mandatory browser enforcement of security requirements that cannot be bypassed."
Why Cookie Prefixes Matter Today
Modern web applications face sophisticated threats: subdomain takeovers, mixed-content exploits, and complex multi-tenant architectures. Cookie prefixes provide defense-in-depth against attack vectors that traditional attributes can't address. When you use __Host- for your session cookies, you're not just following best practicesβyou're eliminating entire classes of vulnerabilities before they can be exploited.
In the following sections, we'll dive deep into the technical requirements of each prefix, explore implementation patterns, and examine the subtle gotchas that can undermine their protection if not handled carefully. Understanding these mechanisms transforms cookie security from a checkbox exercise into a robust, cryptographically-sound defense strategy.
Understanding __Secure- and __Host- Cookie Prefixes
Now that we understand why cookie prefixes exist, let's dive deep into how they actually work. Think of cookie prefixes as naming conventions with enforcement powerβwhen a cookie's name starts with a specific prefix, browsers automatically validate that certain security requirements are met before accepting the cookie.
The __Secure- Prefix: HTTPS-Only Enforcement
The __Secure- prefix is the simpler of the two mechanisms. When you name a cookie starting with __Secure- (like __Secure-sessionId), you're making a contract with the browser: this cookie must only exist in secure contexts.
π― Key Principle: The __Secure- prefix guarantees that a cookie was set over HTTPS and will only be transmitted over HTTPS connections.
For a cookie with the __Secure- prefix to be accepted, it must meet these requirements:
π Must include the Secure flag in its attributes
π Must be set over an HTTPS connection (browsers reject it if sent over HTTP)
Here's what happens at the browser level:
Scenario: Server attempts to set __Secure-token cookie
HTTP Request β Browser Validation β Result
βββββββββββββββββββββββββββββββββββββββββββββββββ
HTTPS + Secure flag β Prefix check Cookie accepted
β Secure flag
β HTTPS origin
HTTP + Secure flag β Prefix check Cookie REJECTED
β Secure flag (Not over HTTPS)
β HTTP origin
HTTPS + No Secure β Prefix check Cookie REJECTED
β Missing flag (Missing Secure)
β HTTPS origin
β οΈ Common Mistake: Setting Set-Cookie: __Secure-session=abc123; Path=/ over HTTPS without the Secure flag. The browser will silently reject this cookie because the prefix contract is violated. β οΈ
π‘ Real-World Example: Imagine you have a staging environment on http://staging.example.com and production on https://example.com. If you use __Secure- prefixed cookies, they'll work in production but automatically fail in staging, preventing accidental security downgrades.
The __Host- Prefix: Domain-Locked Protection
The __Host- prefix takes security several steps further. When you name a cookie __Host-sessionId, you're not just requiring HTTPSβyou're locking that cookie to the exact host that set it, with no ability for subdomains to interfere.
π― Key Principle: The __Host- prefix creates a cookie that is bound to a single, specific hostname with no domain ambiguity.
For a cookie with the __Host- prefix to be accepted, it must meet these stricter requirements:
π Must include the Secure flag (inherits __Secure- requirement)
π Must be set over HTTPS (inherits __Secure- requirement)
π Must NOT have a Domain attribute (this is the critical difference)
π Must have Path=/ (root path only)
Let's visualize the domain-locking protection:
Domain Hierarchy:
example.com (main site)
|
ββββββ΄βββββ¬ββββββββββ
| | |
api. admin. evil.
example example example
.com .com .com
__Secure-token (has Domain=example.com):
β Can be set by example.com
β Can be set by admin.example.com
β Sent to ALL subdomains
β οΈ Vulnerable to subdomain injection
__Host-token (NO Domain attribute):
β Can ONLY be set by example.com
β admin.example.com CANNOT set it
β ONLY sent to example.com
β
Protected from subdomain attacks
This protection is crucial for preventing subdomain cookie injection attacks. Consider this scenario:
π‘ Real-World Example: Your main application runs at bank.com with a session cookie. An attacker compromises legacy.bank.com (an old, unmaintained subdomain). Without __Host-, the attacker could set Set-Cookie: session=malicious; Domain=bank.com; Secure; Path=/ from the compromised subdomain, potentially overwriting your legitimate session cookie. With __Host-session, this attack fails because the Domain attribute isn't allowedβthe cookie is permanently bound to bank.com only.
π€ Did you know? The Path=/ requirement for __Host- isn't arbitrary. It prevents path-based cookie isolation attacks where different paths on the same host might have different security boundaries.
Browser Enforcement Mechanisms
Browsers enforce these prefix requirements during cookie creation validation. When a Set-Cookie header arrives, the browser performs this validation sequence:
1. Parse cookie name
2. Check for prefix (__Secure- or __Host-)
3. If prefix found:
a. Validate connection security (HTTPS required)
b. Validate Secure flag presence
c. If __Host-: validate NO Domain attribute
d. If __Host-: validate Path=/
4. If ANY validation fails β REJECT entire cookie
5. If all pass β Accept and store cookie
This validation is non-negotiableβthere's no fallback or partial acceptance. The cookie is either completely valid or completely rejected.
β οΈ Common Mistake: Assuming older browsers will gracefully degrade. Browsers that don't support prefixes will accept the cookie without enforcement, potentially creating security inconsistencies. Always check browser compatibility for your user base. β οΈ
Choosing Between __Secure- and __Host-
How do you decide which prefix to use? Consider these decision criteria:
Use __Secure- when:
π― You need the cookie to work across subdomains (e.g., SSO tokens shared between app.example.com and api.example.com)
π― You need flexible path scoping
π― Your primary concern is preventing HTTP downgrade attacks
Use __Host- when:
π― The cookie should be strictly isolated to one specific hostname π― You want maximum protection against subdomain compromise π― The cookie contains highly sensitive data (session tokens, CSRF tokens) π― You control the exact host and don't need subdomain sharing
π Quick Reference Card:
| Feature | __Secure- | __Host- |
|---|---|---|
| π Requires Secure flag | β Yes | β Yes |
| π Requires HTTPS | β Yes | β Yes |
| π Domain attribute | β Allowed | β Forbidden |
| π Path requirement | β Any path | β Must be / |
| π Subdomain protection | β No | β Yes |
| π― Use case | Cross-subdomain cookies | Single-host cookies |
π‘ Pro Tip: For authentication session cookies, __Host- is almost always the better choice. The stricter binding provides defense-in-depth against sophisticated attacks involving subdomain takeover or DNS rebinding.
β
Correct thinking: "My session token only needs to work on the main domain, so I'll use __Host-session for maximum protection."
β Wrong thinking: "I'll use __Secure- for everything because it's simpler and less restrictive." (You're giving up valuable security guarantees!)
The beauty of cookie prefixes is that they're declarative securityβby choosing the right name, you automatically get browser-enforced guarantees that are impossible to accidentally misconfigure later. In the next section, we'll see how to implement these prefixes in practice and avoid common implementation pitfalls.
Implementing Cookie Prefixes and Common Pitfalls
Now that you understand the theory behind cookie prefixes, let's dive into practical implementation. This section will show you exactly how to set these cookies correctly and help you avoid the common traps that even experienced developers fall into.
Correct Implementation Examples
When implementing __Secure- prefixed cookies, you must ensure the Secure attribute is present. Here's what a proper Set-Cookie header looks like:
Set-Cookie: __Secure-sessionid=abc123; Secure; HttpOnly; SameSite=Strict
This cookie can include Domain and Path attributes without issue:
Set-Cookie: __Secure-token=xyz789; Secure; HttpOnly; Domain=example.com; Path=/api; SameSite=Lax
For __Host- prefixed cookies, the requirements are more restrictive. Here's a correctly formed __Host- cookie:
Set-Cookie: __Host-user_session=def456; Secure; HttpOnly; Path=/; SameSite=Strict
π― Key Principle: The __Host- prefix creates a "locked down" cookie that can only exist at the exact origin (no subdomain sharing) and at the root path only.
π‘ Pro Tip: Use __Host- cookies for your most sensitive authentication tokens, and __Secure- cookies for other secure data that might need subdomain access.
Common Implementation Mistakes
β οΈ Common Mistake: Mistake 1: Setting __Host- cookies with a Domain attribute β οΈ
β WRONG:
Set-Cookie: __Host-session=abc123; Secure; Path=/; Domain=example.com
This will be rejected by the browser. The __Host- prefix explicitly forbids the Domain attribute to prevent subdomain attacks.
β
CORRECT:
Set-Cookie: __Host-session=abc123; Secure; Path=/
β οΈ Common Mistake: Mistake 2: Using non-root paths with __Host- cookies β οΈ
β WRONG:
Set-Cookie: __Host-api_token=xyz789; Secure; Path=/api
The browser will reject this. __Host- cookies must use Path=/ only.
β
CORRECT:
Set-Cookie: __Host-api_token=xyz789; Secure; Path=/
β οΈ Common Mistake: Mistake 3: Forgetting the Secure attribute β οΈ
β WRONG:
Set-Cookie: __Secure-preference=dark_mode; HttpOnly
Both __Secure- and __Host- prefixes require the Secure attribute. Without it, the cookie is rejected.
β
CORRECT:
Set-Cookie: __Secure-preference=dark_mode; Secure; HttpOnly
β οΈ Common Mistake: Mistake 4: Setting prefixed cookies over HTTP β οΈ
Even if your Set-Cookie header is perfectly formatted, trying to set a __Secure- or __Host- cookie over an insecure HTTP connection will fail. These cookies can only be set via HTTPS.
Legacy System Migration Strategies
Migrating existing cookies to use prefixes requires careful planning. Here's a phased approach that minimizes disruption:
Phase 1: Dual Cookie Strategy
Set-Cookie: session_id=abc123; Secure; HttpOnly; Path=/
Set-Cookie: __Host-session_id=abc123; Secure; HttpOnly; Path=/
Set both the old and new prefixed versions simultaneously. Your application should read from the prefixed version if present, falling back to the legacy version.
Phase 2: Grace Period
Maintain this dual approach for a period that exceeds your longest session timeout (typically 30-90 days). This ensures all active users receive the new cookie.
Phase 3: Cutover
Once the grace period expires, update your application to only read and write the prefixed version:
// Server-side example
if (cookies['__Host-session_id']) {
sessionId = cookies['__Host-session_id'];
} else if (cookies['session_id']) {
// Legacy cookie found - issue new prefixed cookie
sessionId = cookies['session_id'];
setNewPrefixedCookie(sessionId);
}
π‘ Real-World Example: When GitHub migrated to __Host- prefixed cookies for user sessions, they maintained backward compatibility for 90 days to ensure no users were logged out unexpectedly during the transition.
Testing and Debugging Cookie Prefixes
When implementing cookie prefixes, thorough testing across browsers is essential. Here's a systematic approach:
Browser DevTools Inspection
π§ In Chrome/Edge DevTools:
- Open Application tab β Cookies
- Look for warnings (shown with β οΈ icon) next to rejected cookies
- Check the "Secure" column to verify HTTPS-only cookies
π§ In Firefox DevTools:
- Open Storage tab β Cookies
- Rejected cookies appear briefly then disappear
- Check the Console for cookie-related warnings
Common Debug Scenarios
Browser console warning:
"Cookie __Host-session was rejected because it is marked as __Host-
but does not have Path=/"
This tells you exactly what's wrong. Fix by setting Path=/.
Automated Testing
Create integration tests that verify cookie attributes:
## Python example using requests
response = requests.get('https://example.com/login')
cookie = response.cookies.get('__Host-session')
assert cookie is not None, "Cookie not set"
assert cookie.secure == True, "Cookie missing Secure flag"
assert cookie.path == '/', "Cookie path must be /"
assert cookie.domain == '', "__Host- cookies must not have Domain"
π€ Did you know? Some browsers provide different levels of support for cookie prefixes. While all modern browsers support both __Secure- and __Host-, always check caniuse.com for the most current compatibility data.
Best Practices for Choosing Cookie Prefixes
Selecting the right prefix depends on your security requirements:
π Quick Reference Card:
| π― Use Case | π Recommended Prefix | π Rationale |
|---|---|---|
| π Session tokens | __Host- | Maximum protection, no subdomain access needed |
| π« Auth tokens | __Host- | Prevents subdomain takeover attacks |
| βοΈ User preferences | __Secure- | Secure transmission, may need subdomain sharing |
| π Analytics data | __Secure- | Needs to work across subdomains |
| π Multi-tenant apps | __Secure- | Domain flexibility required |
Decision Tree:
Does your cookie contain authentication data?
β
ββ YES β Is subdomain access needed?
β β
β ββ NO β Use __Host-
β ββ YES β Use __Secure-
β
ββ NO β Does it contain sensitive data?
β
ββ YES β Use __Secure-
ββ NO β Consider if prefix is needed
π‘ Remember: More restrictive is generally better for security. When in doubt, try __Host- first, and fall back to __Secure- only if you genuinely need Domain or Path flexibility.
SUMMARY
You now understand how to implement cookie prefixes correctly in production environments. You've learned that __Secure- cookies require only the Secure attribute and HTTPS, while __Host- cookies additionally require Path=/ and no Domain attribute. You've seen the most common mistakes developers make and how to avoid them.
π Quick Reference Card:
| β Requirement | π __Secure- | π __Host- |
|---|---|---|
| Secure attribute | β Required | β Required |
| HTTPS only | β Required | β Required |
| Path=/ | βͺ Optional | β Required |
| No Domain | βͺ Optional | β Required |
| Protection level | π Medium | ππ High |
β οΈ Critical Points to Remember:
- Both prefixes require HTTPS connections
- __Host- cookies provide stronger isolation but are less flexible
- Always test across browsers before deploying to production
- Use a gradual migration strategy for existing applications
Practical Next Steps:
Audit your current cookies: Review all cookies your application sets and identify candidates for prefixes, starting with session and authentication cookies
Implement in staging first: Test your prefixed cookies in a staging environment with real user workflows to catch edge cases
Monitor and measure: Track cookie rejection rates and authentication errors during and after migration to ensure smooth rollout
By implementing cookie prefixes, you're adding a significant layer of defense against cookie-based attacks and demonstrating security best practices in your web application.