#GTMTips: Cross-Domain Tracking in Google Analytics 4

This guide instructs you how to setup cross-domain tracking in Google Analytics 4, and how to handle some of the awkward implementation issues it can lead to.

Setting up cross-domain tracking in Google Analytics 4 has been well-documented.

The main departure from Universal Analytics is how cross-domain measurement is something you configure through the Google Analytics user interface rather than through implementation and JavaScript.

While this approach is obviously beneficial especially for those who lack the know-how or the resources to configure the JavaScript trackers, it does lead to problems, too.

In this article, I want to tackle these edge cases. They are:

  1. How to prevent the _ga cookie update from cross-domain tracking from messing with pre-existing client identifiers on the site.
  2. How to configure cross-domain tracking manually in case the automatic system doesn’t work or you want more control over the deployment.

Tip 128: Setting up cross-domain tracking in Google Analytics 4

If you recall, cross-domain tracking means that for websites collecting to the same Data Stream in Google Analytics 4, the client and session identifiers are preserved when the user moves from one domain to another.

If the domains are subdomains of the same main domain, you do not have to configure cross-domain tracking, as the Google Analytics 4 cookies will be written on the main domain by default. This way they are available to all subdomains by default.

Cross-domain tracking adds a URL parameter to all outbound links from one configured domain to another configured domain.

This URL parameter is a hash of the relevant identifiers, and the domain receiving the traffic will be able to take these identifiers and save them as new first-party cookies on the domain. This way the identifiers are persisted from one domain to the next.

How to setup cross-domain tracking

While this is explained quite clearly in the official documentation, I’ll revisit the steps here.

  1. Make sure that all domains that you want to include in cross-domain tracking have data collection configured to the same Measurement ID (G-XXXXXXX). This is imperative. Cross-domain tracking only works if all the domains collect data to the same data stream.

  2. In the Data Stream settings in Google Analytics 4, click More Tagging Settings.

  3. In the overlay that appears, click Configure your domains.

For example, to configure both simoahava.com and gtmtools.com for cross-domain traffic, I would populate the list like this:

To test this, go to one of the domains collecting to your data stream. Then, click a link that points to some domain you added to the list above.

If all goes as planned, you should see a new URL parameter with _gl followed by a bunch of hashes. Each hash is prefixed with the cookie name. In the example above, we have the following hashes:

  • *_gcl_aw*: Google Ads click identifier.
  • *_ga*: Google Analytics client identifier.
  • *_ga_XXXXXXXXX*: Google Analytics 4 session cookie.
  • *_fplc*: Cross-domain cookie for server-managed client identifiers when using server-side Google Tag Manager.

If you don’t see the _gl parameter in the URL, it means that you’ve either misconfigured the domain name(s) in the cross-domain tracking settings (double-check!) or that there’s something messing with the automated link decoration and you need to employ a manual approach instead (read on!).

The final thing to test is to compare the cookie set on the source domain with those set on the target domain. Assuming you’ve got all the same technologies running on the target domain, you should see the cookies with matching values between the domain that sent the traffic and the domain the traffic landed on.

For example, if you have a Google Analytics 4 config setup on the target domain, collecting to the same data stream as the GA4 set up on the source domain, both _ga and _ga_XXXXXXXXXX should have identical values across the two domains.

If you’re serving a server-side Google Tag Manager container from the same account as the one served on the source domain (and you’re collecting to the same Measurement ID), you should see the FPLC cookie with matching values between the two domains.

And if you’ve got a Google Ads conversion tag running on the target domain, you should see the _gcl_aw cookie set with the same click identifier as the one that was set on the source domain.

All this testing serves a purpose: the traffic being sent from the source domain and the traffic being sent from the target domain should have the same identifiers across the relevant hits.

If you see differences, then there’s an implementation mistake somewhere.

How to account for a multi-property or multi-platform setup

Cross-domain tracking overwrites the Google Analytics cookie(s) on the target domain with values from those on the source domain. This is necessary to align the concept of a “user” and “session” across the two domains.

However, what if you already have those cookies configured on the target domain, maybe even with an extensive history of data collected with the identifiers? Too bad! Those values will be bulldozed by the ones from the source domain.

For this reason, if you have other trackers on the target domain too, for which you want to avoid cross-domain tracking from overwriting the identifiers, you need to protect those other trackers by namespacing their cookies.

This is different from Universal Analytics, where the recommended practice was to rename the roll-up tracker cookie so that any pre-existing cookies would be left untouched.

Unfortunately, with Google Analytics 4, the cross-domain linker only works with the default cookie name.

Naturally, this means that if you want to implement cross-domain tracking, you’re going to lose your local GA history anyway, unless you implement some complicated shenanigan to copy the cookie value from _ga to _local_ga first before the cross-domain linker has time to do its work.

Yes, this is super awkward and really an obstacle for setting up automated cross-domain tracking if you want to preserve history on the target site!

If you don’t mind starting from scratch with the non-cross-domain Google Analytics 4 properties, you can set their cookie_prefix field to some values. After that, all tags that use this config will have their _ga cookie prefixed with whatever you added to that field.

This is how it looks like once the cookies are set:

How to configure cross-domain tracking manually

If cross-domain tracking doesn’t work automatically, if you want more control over which cookies are overwritten, or if you just don’t trust any automated configurations and want a more manual touch for setting things up, you can always set up cross-domain tracking manually.

Unfortunately, it’s really awkward. For some reason, there’s no built-in API in gtag.js or Google Tag Manager which would allow us to generate the linker parameter (the one with the _gl) manually. Instead, even per the official documentation, you’ll need to build the query string manually, adding cookie values as URL parameters to the outbound links, and then grabbing them from the URL on the target site.

It’s just not sophisticated. At all. It requires a crazy amount of manual labor from the site admin.

Here’s the process you’ll need to do:

  1. On the source site(s), write an event listener that listens to all link clicks (or relevant interactions) targeted at domains you want to pass the identifiers to.
  2. When such an event happens, prevent the default action (redirect) of the event in the listener callback.
  3. Pull in the first-party cookie values that you want to pass to the target page, and append them to the original link URL as query parameters.
  4. At the very least, add the current timestamp to the URL as well (more on this below). If possible, write a simple browser fingerprint as well.
  5. Load the target URL with the URL parameters in place.
  6. On the target page, check if the timestamp in the URL is less than one minute old (for example) and, optionally, that the browser fingerprint matches the current browser.
  7. Before any tags have time to fire, run a script that pulls the values from the URL and writes them into first-party cookies on the target URL.

On (6), we’re trying to prevent a scenario where the user shares the URL in social media (or similar), after which anyone who follows the link will be considered as the same user as the one who shared the link.

As an option to (7), if you’re only concerned about Google Analytics 4 (client-side) tracking, you could also load the URL parameter values directly into the GA4 configuration with something like:

gtag('config', 'G-12345', {
  // Namespace roll-up trackers
  cookie_prefix: 'roll-up',
  // Pull in the Client ID from the URL
  client_id: (new URLSearchParams(document.location.search)).get('client_id'),
  // Pull in the Session ID from the URL
  session_id: (new URLSearchParams(document.location.search)).get('session_id')  
});

This is just a very simple example. It sucks that you have to do all this work manually. Google Analytics 4 would really need an API for grabbing the linker parameter. Hopefully soon I can update this article happily with details on how to generate the linker parameter manually.

Summary

While it’s great that cross-domain tracking works without having to run code on the site, it would be equally great if there were a viable manual option, too.

The biggest issues with cross-domain tracking in Google Analytics 4 are:

  • No viable way to protect cookies from being overwritten on the target domain.
  • No viable way of manually generating the linker parameter.

Both of these scenarios require, ironically, quite a bit of manual work to resolve. And because there’s no way to programmatically generate the linker parameter, this manual work will almost always be substandard to what the gtag.js library generates automatically.

Cross-domain tracking is such a fundamental part of any web analytics platform. I’m glad that Google Analytics 4 has done its best to demystify it by externalizing the setup to the GA4 user interface.

The next step is to include sufficient means to set it up (or configure it) manually, too.

Let me know in the comments if you have questions about setting cross-domain tracking up in Google Analytics 4! I’m aware I didn’t include a full example of how to do the cross-domain decoration manually, but I’m holding off for the chance that we’ll have the means to generate the linker parameter manually.