Sasha Romijn

The definitive guide to cookie domains and why a www-prefix makes your website safer

Restricting access to cookies is essential for security in many web apps. For example, the session ID, the secret token used to identify a particular session, is typically stored in a cookie. Cookies have several important settings. Previously, I discussed the secure flag. This time, let’s dive into the cookie domain.

The cookie domain is an important security feature, probably even more important than the secure flag. It tells the browser that this cookie must only be sent to matching domains. Matching however, can happen in several ways. Perhaps domain is a bit of a misnomer: this can be any host name, like

With this in mind, I did some digging into the exact workings of cookie domains, and was surprised to find this less straight forward than I had expected. And, it turns out Internet Explorer’s RFC-incompliant behaviour makes it safer to host your websites with a www-prefix, so instead of

Update: this post was updated on April 9, 2014, to reflect that Internet Explorer misbehaves with domain-less cookies, as learned from this blog post. Previously, I concluded that a www-prefix makes no difference, with this new knowledge, a www-prefix is safer.

Key points

  • When not setting an explicit domain for a cookie, the default in most browsers is to only send the cookie when the domain matches exactly. However, Internet Explorer violates the RFC, and will send it to all subdomains as well.
  • The most compatible way of having a cookie visible to this domain and all sub domains, is to prefix it with a dot, like for and all sub domains (and their sub domains, etc.)
  • If you set a cookie domain without the dot prefix, like, this will still be treated as “ and all sub domains”. Additionally, this is invalid in very old implementations.
  • Setting an explicit cookie domain may therefore actually decrease security, as you will now include all sub domains in all browsers. For Internet Explorer, there is no difference.
  • To keep your cookies safe, host your websites with a www-prefix, so instead of, as in the latter case, Internet Explorer will also send the cookies to any (perhaps malicious) subdomain of (in violation of the RFC).
  • Regardless of your cookie domains, if you place or modify sensitive data in sessions, make sure to rotate the session ID to prevent fixation attacks.
  • None of this excuses you of the responsibility to keep your domain clean from untrusted hosts. If you want to host untrusted user content, place it under a different domain.

I have tested this behaviour with the latest Firefox, Safari and Chrome under Mac OS X.

Domain matching specification

Cookies were defined in three RFCs, from 1997, 2000 and 2011, each succeeding the other.

RFC 2109 and RFC 2965

RFC 2965, published 2000, defines that exact matches are always considered a match, unsurprisingly. For partial matches, it states (where A is the request host name, and B is the cookie domain):

A is a HDN string and has the form NB, where N is a non-empty name string, B has the form .B’, and B’ is a HDN string. (So, domain-matches but not

In other words, if the cookie domain is, and the browser performs a request to or, the cookie is not a match and will not be sent. If the cookie domain is however, it will match.

However, it also specifies:

If an explicitly specified value does not start with a dot, the user agent supplies a leading dot.

So if you would set the cookie domain to in the header, the user agent should treat the cookie domain as, and it will match with

Now, it may be that RFC 2965 was referring only to the Set-Cookie2 header. However, the older RFC 2109, from 1997, differs in only one part:

An explicitly specified domain must always start with a dot.

So in RFC 2109, setting a cookie domain to is simply invalid – you must set it to for it to be valid, which means it will also match

However, this leaves an interesting question. What happens if no cookie domain was specified at all? Both state:

Domain: Defaults to the effective request-host. (Note that because there is no dot at the beginning of effective request-host, the default Domain can only domain-match itself.)

Therefore, in RFC 2109/2965 implementations, if a request is done to, and a cookie is set without an explicit domain, the domain defaults to – not to! In that case, it will not match So a cookie set by a request to without an explicit domain, is very different compared to explicitly setting the domain in the cookie, as the latter is treated as

RFC 6265

The successor of RFC 2965, RFC 6265, was published in 2011. This means we will still encounter browsers that have not adopted to this specification. It has a somewhat different approach in section 5.1.3:

The domain string is a suffix of the string and the last character of the string that is not included in the domain string is a %x2E (“.”) character.

In other words, the cookie domain matches the request. So what about using

Note that a leading %x2E (“.”), if present, is ignored even though that character is not permitted

So is identical to However, there is another special condition:

If the server omits the Domain attribute, the user agent will return the cookie only to the origin server.

So if a request is made to, a cookie is set without an explicit domain, the user agent must match it only to, and not to The RFC notes that not all user agents might handle this correctly.

And then there is Internet Explorer

Unfortunately, this is only what the RFC specifies. Internet Explorer intentionally deviates from this (see Q3), although the reason for this is unknown to me.

In Internet Explorer, a cookie without an explicit domain is also sent to all subdomains. That deviation means that a cookie set without a domain from is more at risk than In the former case, Internet Explorer will still send it to any subdomains of, in the latter case only to subdomains of, which will be less common. In the non-www case, just a single malicious host under that domain compromises all cookies you’ve set.

I built a simple test case for this scenario and users of Internet Explorer 9 and 11 reported that their browsers misbehaved.


Although the definitions are somewhat different, we can simplify it for any of these implementations as:

  • When no domain is set in the cookie, the cookie should only match the exact host name of the request. No sub domains, no partial matches. This means simply not including the domain attribute – it is not valid to set an empty domain attribute. Unfortunately, Internet Explorer appears to treat this as the host name along with any subdomains.
  • When setting a domain in the cookie, the safe choice is to have it preceded by a dot, like The cookie will match with all sub domains.
  • Setting a cookie domain without a preceding dot, like, is invalid in RFC 2109 implementations, and will produce the same behaviour as with a preceding dot on other implementations. There is no way to restrict a cookie to a specific explicitly set domain, without sub domains being included.

Other worthwhile observations:

  • In all RFCs, a specified cookie domain must match the current host name, per normal matching. Setting a cookie for in a response from is not valid, as a cookie with domain does not match, the former being more specific.
  • In RFC 6265, domains are explicitly lower cased when parsing the Set-Cookie header.

Malicious hosts under your domain

Although cookie domains do help to limit the scope of your cookies, it is still best to avoid having untrusted hosts under your domain. This is why GitHub pages are hosted under, not, for example. The risk is that any page under the same domain, can always place cookies at the domain-level.

One way to abuse this, which can not be fixed with any domain setting, is session fixation. Let’s say I control, and a Django site on uses session cookies. I trick the victim into visiting, and set a sessionid cookie for Remember, you can always set a cookie for a less specific domain, so this is allowed. I use a valid session ID, which I just got from the real Then, the victim visits and the browser sends my valid sessionid cookie, because matches requests for The user logs in, and now their session, which I can access because I know the session ID, gives me access to their account.

Fortunately, this attack does not actually work in Django, because Django replaces the session ID when something significant happens, like a login or logout. At that point, the session ID I had is no longer useful. Remember to rotate the session ID as well for your own apps, when you add sensitive data to a session.

Shameless plug: an additional method of securing your sessions is my package django-restricted-sessions. In that case, the session would already have been reset as soon as the victim visits and sends the planted sessionid cookie, unless the IP and user agent matches as well.

What about third party cookies?

Third party cookies are cookies placed for different domains than the page you are viewing. For example, if you visit, and that results in a cookie being set for, we consider the latter a third party cookie.

Third party cookies are not prevented by domain matching, because the domain matching happens at the request level. A single page may result in many requests to many different domains. For each of those requests, the browser runs its cookie matching code to see which cookies to include. In the case I described, it might be that includes an image from When loading the image, the browser may receive or send cookies belonging to, because that’s the host name for that particular request. If other pages also include an image hosted on, the browser will send the cookies it received from previous requests. This is how third party cookies can be used for tracking across different websites.

Many browsers have an option to disable third party cookies. I haven’t looked into it, but logical way to do this, would be to add the restriction that cookies must match the domain of the URL of the page the user is viewing.

Further reading

From this blog, you may also like: