Husband | Father | Analytics developersimo (at) simoahava.com

Cross-domain Tracking Across Subdomains

Wait. What? Why write an article about something that should work by default in Universal Analytics? I mean, here’s a screenshot from the guide I just linked to in the previous sentence:

There it is. Clear as day: “Tracking users across subdomains does not require any additional configuration.” Also, some of the recent, excellent guides to cross-domain tracking, written by E-Nor and Lunametrics enforce the same: you just need a default Universal Analytics Tag in Google Tag Manager.

Well, unfortunately, that’s not true. You do need a configuration setting, but it’s thankfully very simple.

TL;DR: In the Fields to Set of your Universal Analytics (Page View) Tag, add the following setting:

But since you’re awesome, you didn’t come here for the dirty truth, you want to know why this is necessary. So read on!

How Universal Analytics writes cookies

To track the same user and session across any two pages, these pages require the following:

A tracker object that tracks to the same Google Analytics Property ID (UA-XXXXXX-Y)

A _ga cookie that has the same Client ID

Client ID is what is used to stitch the hits in the session together, and also to stitch sessions the same user has had into a common level of aggregation (the GA concept of a user).

Now, if you don’t have any customizations in your Universal Analytics Tag, here’s the logic with which cookies are written:

If the hostname of the page (http://this is the hostname/home/) starts with www., the “www” is stripped, and the cookie is written on what remains. So, in the case of www.simoahava.com, the cookie is written on .simoahava.com.

On all other hostnames, the cookie is written on the hostname itself, prefixed with a period. So, test.simoahava.com would retrieve a _ga cookie written on .test.simoahava.com

If prefixed with a period, like the _ga cookie is, the cookie can be used on all hostnames that contain this string. So the .simoahava.com cookie can be used by test.simoahava.com, www.simoahava.com, simoahava.com, and immortal.genius.simoahava.com, for example.

The .test.simoahava.com cookie, on the other hand, can be used by www.test.simoahava.com, but not by www.simoahava.com, as the latter does not contain the string “test.simoahava.com”.

Can you see where I’m going with this?

If traffic is from www.simoahava.com to test.simoahava.com, the _ga cookie is shared, and all is well. Right?

However, if the visitor first visits test.simoahava.com, and then moves to www.simoahava.com, these two domains will have different_ga cookies, and thus different Client IDs, and thus the user will be a different user with a new session!

So clearly, in many, many cases, this will lead to problems. We need to somehow ensure that the _ga cookie is always written on .simoahava.com, so that it can be used by all subdomains, regardless of if they have “www.” or something else as a prefix.

cookieDomain : auto

The answer is in the cookieDomain setting. When you set cookieDomain to auto, the following will happen with a (fictional) domain like www.simoahava.co.uk:

GA tries to write the cookie on .co.uk, which is the first possible root domain candidate. This fails because the browser is not authorized to write a cookie on a top-level domain like that.

Next, GA tries to write the cookie on .simoahava.co.uk, which is the next possible root domain candidate. This works because that’s a valid domain to write the cookie on.

So there’s a recursive algorithm, which tries to write the cookie, starting from the most generic domain-level (the top-level domain), and stopping once it succeeds. What should be left is the root domain, and thus the cookie will be available to all subdomains.

Yay!

The new and improved recommendation

Here’s the improved recommendation for cross-domain tracking across subdomains:

Always default to having cookieDomain : auto in your tracker settings. In GTM, I’ve shown an example in the beginning of the post.

In the Universal Analytics tracking code, the snippet would look like this: