Understanding Cross-Subdomain Cookie Sharing: Can subdomain.example.com Set Cookies for example.com?


2 views

When dealing with cookies in web development, the Domain attribute plays a crucial role in determining cookie scope. Let's examine the technical specifications and practical implications.

RFC 2965 (now largely superseded by RFC 6265) states that cookies set with Domain=example.com will automatically have a dot prepended, effectively becoming .example.com. This means:

// Cookie set from subdomain.example.com
Set-Cookie: session=abc123; Domain=example.com; Path=/
// Browser interprets as:
Set-Cookie: session=abc123; Domain=.example.com; Path=/

Here's how cookie sharing actually works between domains:

// Setting cookie from subdomain that parent domain can read (PHP example)
setcookie(
    "shared_cookie",
    "cross_domain_value",
    time() + 3600,
    "/",
    ".example.com",  // Note the leading dot
    true,  // Secure flag
    true   // HttpOnly flag
);

When working with cookies via JavaScript, you need to be explicit about the domain:

// JavaScript example for cross-subdomain cookies
document.cookie = "user_prefs=dark_mode; domain=.example.com; path=/; secure";

Developers often encounter these issues:

  • Forgetting the leading dot in the domain (though modern browsers may handle this)
  • Not setting the Path attribute correctly
  • SSL requirements for Secure cookies

Use this simple test script to verify cookie sharing:

// Test script for cookie availability
function checkCookie() {
    console.log("Cookies available:");
    console.log(document.cookie);
}

// Set from subdomain
document.cookie = "test_cookie=value; domain=.example.com; path=/";
checkCookie();

When implementing cross-subdomain cookies:

  • Always use the Secure flag for HTTPS sites
  • Consider SameSite attributes (Strict/Lax/None)
  • Be cautious with sensitive data in shared cookies

For complex cross-domain scenarios, consider:

// Using localStorage with postMessage for cross-domain communication
window.parent.postMessage({
    type: 'storage',
    key: 'user_token',
    value: 'abc123'
}, 'https://example.com');

Remember that the exact behavior might vary slightly between browsers, so always test your implementation across different environments.


RFC 2965 (and its successor RFC 6265) creates apparent contradictions when dealing with cookie domain attributes. Here's the technical reality:

// Example of cookie setting from subdomain
Set-Cookie: shared_cookie=value; Domain=.example.com; Path=/; Secure

The actual browser behavior follows these rules:

  • Explicit Domain Declaration: When subdomain.example.com sets Domain=.example.com, the cookie will be accessible to:
    • subdomain.example.com
    • any.other.sub.example.com
    • example.com (root domain)
  • Implicit Domain Handling: If no Domain is specified, modern browsers will restrict the cookie to the exact host that set it

Node.js Express Implementation

// Setting from subdomain to be accessible by root
app.get('/set-shared-cookie', (req, res) => {
  res.cookie('app_token', 'xyz123', {
    domain: '.example.com',
    secure: true,
    httpOnly: true
  });
  res.send('Cookie set for entire domain');
});

PHP Implementation

// PHP setcookie() example
setcookie(
  "user_prefs", 
  json_encode($preferences),
  [
    'expires' => time() + 86400,
    'path' => '/',
    'domain' => '.example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
  ]
);

When implementing cross-subdomain cookies:

  • Always use Secure flag for HTTPS-only transmission
  • Include HttpOnly to prevent XSS attacks
  • Consider SameSite attributes (Lax/Strict) for CSRF protection
  • Validate cookie scope requirements - don't over-share

Test results across major browsers (Chrome 120+, Firefox 115+, Safari 16+):

Browser Handles .example.com Handles example.com
Chrome Converts to .example.com
Firefox Rejects without dot
Safari Converts to .example.com

To verify cookie behavior:

// JavaScript cookie inspection
console.log(
  All cookies: ${document.cookie}\n +
  Specific cookie: ${getCookie('shared_cookie')}
);

function getCookie(name) {
  return document.cookie
    .split('; ')
    .find(row => row.startsWith(name))
    ?.split('=')[1];
}

Using Chrome DevTools: Application > Storage > Cookies shows exact domain scoping

Common patterns where this matters:

// Microservices architecture example
auth-service.corp.example.com   → Sets auth cookie
api-service.corp.example.com    → Reads auth cookie
app.example.com                 → Reads auth cookie