Setting cookies to subdomains in JavaScript
Published: 2020-12-29 11:00:42

Setting cookies to subdomains can be very tricky.

I've recently faced a problem of setting a cookie from foo.mycompany.com to all subdomains in a `*.mycompany.com` domain. The solution seemed pretty straightforward: just set a wildcard cookie to `.mycompany.com` (note that the first character is a dot). However, I had to determine the target domain (the actual value of ”`.mycompany.com`”) automatically because my code is fired on tens of thousands of different domains.

And here came the problem: the list of [Top-Level Domains](https://en.wikipedia.org/wiki/Top-level_domain).

# Top-Level Domains and cookies

Let’s consider two similar domains:
* foo.bar.com
* foo.co.uk

JavaScript allows you to set a cookie available to all `bar.com` subdomains from within the `foo.bar.com` subdomain.

However, it won’t let you set a cookie to all `co.uk` subdomains from within the `foo.co.ok` subdomain because `co.uk` is a Top-Level Domain. If it was possible, your browser would send that cookie to all websites available in the British (`co.uk`) domain.

Web browsers don’t offer a way to check if the given string is a Top-Level Domain or not. If such a feature existed, it would help me determine if I can set a cookie to `.co.uk` (which I can’t) or `.bar.com` (which I can).

# List of TLDs in your app (not recommended)

One of the solutions is to store a list of all Top-Level Domains in your app and check your domain against this list. Mozilla Foundation hosts a project called [Public Suffix List](https://publicsuffix.org/list/) which stores all TLD names in one place.

But in reality, keeping the list in your app is just a pain in the ass.

# The “try and check” method (recommended)

There’s an easier solution though: just set a cookie to the domain and check if the browser actually set that cookie. If it didn’t, it’s a Top-Level Domain and we need to try setting a cookie to a subdomain.

Here’s a working example of the code that sets the cookie and copes with the mentioned TLD problem. It’s a modification of the renowned code snippet from an [article about cookies](https://www.quirksmode.org/js/cookies.html) on QuirksMode:

```js
var Cookie =
{
   set: function(name, value, days)
   {
      var domain, domainParts, date, expires, host;

      if (days)
      {
         date = new Date();
         date.setTime(date.getTime()+(days*24*60*60*1000));
         expires = "; expires="+date.toGMTString();
      }
      else
      {
         expires = "";
      }

      host = location.host;
      if (host.split('.').length === 1)
      {
         // no "." in a domain - it's localhost or something similar
         document.cookie = name+"="+value+expires+"; path=/";
      }
      else
      {
         // Remember the cookie on all subdomains.
          //
         // Start with trying to set cookie to the top domain.
         // (example: if user is on foo.com, try to set
         //  cookie to domain ".com")
         //
         // If the cookie will not be set, it means ".com"
         // is a top level domain and we need to
         // set the cookie to ".foo.com"
         domainParts = host.split('.');
         domainParts.shift();
         domain = '.'+domainParts.join('.');

         document.cookie = name+"="+value+expires+"; path=/; domain="+domain;

         // check if cookie was successfuly set to the given domain
         // (otherwise it was a Top-Level Domain)
         if (Cookie.get(name) == null || Cookie.get(name) != value)
         {
            // append "." to current domain
            domain = '.'+host;
            document.cookie = name+"="+value+expires+"; path=/; domain="+domain;
         }
      }
   },

   get: function(name)
   {
      var nameEQ = name + "=";
      var ca = document.cookie.split(';');
      for (var i=0; i < ca.length; i++)
      {
         var c = ca[i];
         while (c.charAt(0)==' ')
         {
            c = c.substring(1,c.length);
         }

         if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
      }
      return null;
   },

   erase: function(name)
   {
      Cookie.set(name, '', -1);
   }
};
```

And here’s how to use it:

```
Cookie.set('test', '123');
```

When we are on `foo.bar.com` domain, the cookie will be available on `*.bar.com` subdomains.

But when we are on `foo.co.uk` domain, the cookie will be available on `*.foo.co.uk` subdomains.

This code works fine on my production environment on thousands of different domains. It’s way easier than storing the list of Top-Level Domains and comparing the current domain to the ones on the list.