You are viewing a preview of this lesson. Sign in to start learning
Back to Web Security: The Modern Browser Model

Secure and Host Cookie Prefixes

Enforce HTTPS-only and domain-restricted cookies through __Secure- and __Host- naming conventions

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.

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.

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- prefixSubdomain injection, path manipulationBrowser enforces origin restrictions
πŸ”’ __Secure- prefixHTTP injection/interceptionBrowser requires HTTPS
πŸ”’ HttpOnly attributeXSS cookie theftBlocks JavaScript access
πŸ”’ SameSite attributeCSRF attacksControls cross-site sending
πŸ”’ Secure attributeNetwork sniffingHTTPS-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."

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.

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.

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.

When implementing cookie prefixes, thorough testing across browsers is essential. Here's a systematic approach:

Browser DevTools Inspection

πŸ”§ In Chrome/Edge DevTools:

  1. Open Application tab β†’ Cookies
  2. Look for warnings (shown with ⚠️ icon) next to rejected cookies
  3. Check the "Secure" column to verify HTTPS-only cookies

πŸ”§ In Firefox DevTools:

  1. Open Storage tab β†’ Cookies
  2. Rejected cookies appear briefly then disappear
  3. 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.

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:

  1. Audit your current cookies: Review all cookies your application sets and identify candidates for prefixes, starting with session and authentication cookies

  2. Implement in staging first: Test your prefixed cookies in a staging environment with real user workflows to catch edge cases

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