DEV Community

Cover image for Who Moved My Cookies? Of Cookies On Subdomains
Jen Chan
Jen Chan

Posted on • Originally published at jenchan.biz

Who Moved My Cookies? Of Cookies On Subdomains

Yes, it's 2025, we had the internet and the encyclopedic knowledge of LLMs at our fingertips, and we were still blocked for 45 minutes.

Maze game for kids cute groovy cookie with pieces of chocolate looking for a way to the gift box

"Maze game for kids cute groovy cookie with pieces of chocolate looking for a way to the gift box" by Maria Kololeva.

The Problem

Imagine, it's possible for a gaming platform to have all of these namespaces:

  • addictinggames.com
  • fun.addictinggames.com
  • cool.addictinggames.com

And other mirror domains could also receive its traffic in the event some of them got blocked by parental controls or libraries, or some no-fun workplaces want to prevent people from playing fun games:

  • addictinggames.wtf
  • addictinggames.lol
  • evenmoreaddictinggames.com
  • playaddicting.fun

Such a system was set up so that if Johnny was logged in at addictinggames.com but his mom decided to block access to the site, he'd would be able to use addictinggamesmirror.com to redirect his session to addictinggames.wtf and continue playing there. ✌

So, we were going to launch a web 3 game at a subdomain, even.more.addictinggames.com... with the intention that sessions could be persisted to even.more.addictinggames.wtf, even.more.additinggames.lol, so on and so forth.

How hard could it be?

It was impossible.

We had previously used subdomains and mirror domains to persist user sessions for authenticating users.

The cookie was just proof that the user was still authenticated -amongst other things- after a redirect.

While all other areas of the platform showed that a user who had logged in and went back to the view was still authenticated, the proxied request returned nothing.

What we tried

We of course did the usual cookie clearing, logging out and logging in and checking the dev tools.

We checked that there wasn't anything on the client side that was setting or eliminating the cookie –an anti-pattern anyway.

Since the cookie was HTTP-Only, there wouldn't have been any way for the client to access it.

We checked that the cookie was being set on the server side.

We compared headers from the proxied request and the original request.

We tried desperately to specify the destination domain for Set-Cookie to be *.addictinggames.com or even.more.addictinggames.com.

Our manager pointed us to the MDN docs on cookies.

As it turns out:

If the Set-Cookie header does not specify a Domain attribute, the cookies are available on the server that sets it but not on its subdomains. Therefore, specifying Domain is less restrictive than omitting it. Note that a server can only set the Domain attribute to its own domain or a parent domain, not to a subdomain or some other domain.
So, for example, a server with domain foo.example.com could set the attribute to example.com or foo.example.com, but not bar.foo.example.com or elsewhere.com (the cookies would still be sent to subdomains such as bar.foo.example.com though).

See RFC 6265 Section 4.1.2.3 on "HTTP State Management Mechanism".

One does not simply set a cookie on a subdomain

✅ We could expect that a server that responds with the Set-Cookie header and no Domain specified, would only set the cookie to the host server of the document, but not at subdomains.

✅ We could expect that a server responding with the Set-Cookie header and Domain specified to addictinggames.com, would allow addictinggames.com and subdomains like fun.addictinggames.com to receive the cookie.

✅ We can also use the Set-Cookie header to set a Domain attribute to fun.addictinggames.com and expect the cookie to be set when visiting fun.addictinggames.com.

❌ We can't use the Set-Cookie header to set a Domain attribute to even.more.addictinggames.com or even.more.addictinggames.wtf.

So it follows that it would be another magnitude of problematic to expect such a cookie to persist on an external mirror subdomain like even.more.addictinggames.wtf.

The Solution

Sometimes web APIs defy logic.

Don't try to set cookies on a subdomain-of-a-subdomain. Just stick with one level or the parent domain.

Instead of dark-launching on even.more.addictinggames.com, we went with more.addictinggames.com.

Ta-da!

Top comments (11)

Collapse
 
shakti_kathpalia profile image
Shakti Kathpalia

We faced a similar issue for a project we're developing. Our laravel backend (a subdomain) generated the cookie that had to be validated in Next.js frontend (another subdomain) and another 3rd party system (yet another subdomain) which only worked with cookies. The Next frontend also authenticated the laravel session on every refresh through an API before fetching user-specific data.

The only solution was to create a cookie in the backend with the domain set to the root domain and voila, everything worked seamlessly.

Collapse
 
jenc profile image
Jen Chan

We were in a very similar situation except proxied by Cloudflare and using Nuxt. I think going through Cloudflare workers added a bit of complexity and cross checking overhead.

Collapse
 
shiva_shanker_k profile image
shiva shanker

Haha, this is so relatable! 😂 Cookie authentication issues are the worst - works perfectly in dev but production mein everything breaks

Collapse
 
jenc profile image
Jen Chan

The struggle with systems you can't see or debug every layer of results in... testing on prod! XD

Collapse
 
mezieb profile image
Okoro chimezie bright

Thanks for sharing helpful tips

Collapse
 
dotallio profile image
Dotallio

Yes, hitting that wall with cookie domains is always so frustrating. Did you end up considering tokens in URL params or localStorage at all for those mirror domains?

Collapse
 
jenc profile image
Jen Chan

I didn't think to put the tokens in the URL params, and I guess the way we were doing it wasn't entirely standard unlike OAuth2 url params including tokens.

The team abused LocalStorage before for less sensitive values so our Veep discouraged that as a practice... which left us with that cookie 😆

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

Man, you summed up months of cookie pain in one post. I've been tripped up by that 'Domain' attribute headache so many times, it's honestly nice seeing it all laid out like this

Collapse
 
jenc profile image
Jen Chan

No kidding, working on that system with 3 layers of backend (MySQL, laravel, cloudflare edge proxy), so many mirror domains and no real way to run it all locally was crazy-making!

Glad I'm not the only one who's baffled and stumped!

Collapse
 
donahere profile image
donahere

Awesome easy-to-follow and meaningful post @jenc 👍👍👍

Collapse
 
jenc profile image
Jen Chan

Thanks! So many things on the job are still hairy mysteries even with LLMs helping to debug!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.