How to Do Cross-Domain Google Analytics (Updated for GA4)

Let’s say you have two completely different primary domains, say,, and, and you want to track them as a single property on Google Analytics. For example, if a user goes from one site to the other, you want that visitor to show up on the same Google Analytics report and not count as two visitors, but rather as a single visitor who crossed domains.  How do you do it?

Update for Google Analytics 4

Since I first wrote this article, Google has introduced Google Analytics 4, which makes cross-domain tracking MUCH easier. In a nutshell:

  1. Go to Admin Settings (gear icon in lower left corner)
  2. Go to Data Streams and click on your site’s stream
  3. Click “Configure tag settings”
  4. Click “Configure your domains”
  5. Add a “condition” for each site you want to track.
  6. That’s basically it!

This is SO much easier than the old way!

For more details, see this blog post from Charles Farina, or his YouTube video.

I’m keeping the legacy article below because it has a technique for removing query strings in the URL bar of the browser.


The problem is that the cookies (or cookie, if you are using Universal Analytics) used by Google Analytics to track visitors won’t get passed across domain names.  This isn’t Google’s fault; it’s just the way it is for security reasons.

The solution that Google has come up with is to pass the cookie information through the link in the form of a query string variable.  Let’s say you want to have a link on to  You would add a query string to the links to so they would look something like this if you are using Universal Analytics:

This URL would appear in the user’s browser after they reach the site.  Personally, I thought this was REALLY UGLY and unacceptable!  Imagine users sharing this link on Twitter!  The query string is even more convoluted and has more parameters if you use Classic Analytics.

In all of my research online, I was unable to find a method to do cross-domain analytics without an ugly parameter in the URL.  So, I came up with my own trick.

In this article, I’ll briefly explain how to set up cross-domain Google Analytics using their latest Universal Tracking code, then I’ll describe by trick to get rid of the ugly query string.

How to Set Up Cross-Domain Universal Analytics

Google has done a pretty good job of documenting how to set up cross-domain analytics, so I won’t repeat the individual steps here. 

If you’re using Google Analytics 4, check out this page for setting up cross-domain analytics (the video is very good).

If you’re using gtag.js code, see this page for setting up cross-domain analytics.

If you’re using analytics.js, see this page for setting up cross-domain analytics.

The code uses JavaScript to modify the links on your page that cross domains and which you want to include in your cross-domain analytics.  The nice thing is that you don’t have to individually modify each link.  Google’s JavaScript code does that for you (Google calls it “decorating” your links… “Uglify” would be a better word.)

However, if your page uses some tricky JavaScript or other non-HTML method to do links, the code might not work and you might have to annotate those links manually.

The one major bump I ran into occurred when I tried to set up the new View and Filters in Google Analytics, as described in the article.  Make sure you have full Edit permissions, not only on the Google Analytics property but also on the account to do this.  If you own the GA property, you probably already have permissions.  Otherwise, get the owner to give you full edit permissions everywhere.

Another thing you’ll want to do which is not mentioned in the Google article is to exclude self-referrals from your reports.  This article tells you how to exclude self-referrals.

If you are using Google Tag Manager and want to set up cross-domain analytics there, check out this article and excellent video on Cross Domain Tracking in Google Tag Manager.

Testing and Debugging

If you’ve done the steps correctly and you click on a cross-domain link that you want to track, you should see the “decorated” URL in the browser window.

I recommend leaving your site in this state and checking your Google Analytics report in a day or two, just to make sure the data is being gathered properly.  You should be able to log into Google Analytics and see your separate domains tracked as if they were part of the same site.  The Behavior Flow diagrams should show traffic going from one domain to the other.

If you are not seeing separate domains in your reports, the problem could be that you have not set up your filters correctly.  By default, Google Analytics does not show the domain name in its reports – it only shows the part after the slash.  So, if you are going from to, then you would see visits from “/buy” to “/cart”.  But, if you are going from to, all of the visits will show up as “/”, which is not helpful.  If that happens, go back and set up your filters.

Don’t proceed unless you have cross-domain analytics working.

Removing the Ugly Query String Variable

If your cross-domain analytics are working, then you’re ready to kill the ugly query string.  I use a JavaScript library to read in the site’s URL from the browser, modify it to remove the “_ga” variable, and put it back. 

You could write some simple JavaScript code yourself, but URLs are deceptively complex beasts.  Removing one query string variable while leaving the others intact is not as trivial as it sounds if you account for all types of URLs with other query variables, etc.  So, I went for a third-party query string manipulation plugin called URL.js.  Go to that site and click on “Build”, then check the following parameters:

URI.js - Custom Build-cropped

For our purposes, there are many settings that would probably work just fine.  These are just the settings that I used which worked.

Next, click the “Build!” button and copy the code into a file called uri.js and upload it to your website.

On all of the destination pages that you have cross-domain links to, add this code to the bottom of the page before the closing <body> tag:

<script type="text/javascript" src="/scripts/uri.js"></script>

<script type="text/javascript">
            $(window).load(function() {
            url = URI(window.location.href).removeSearch("_ga");
            history.replaceState("state", "", url);

In the first line, replace the src path to wherever you put your uri.js file.  Also, this code uses jQuery to trigger off of $(window).load.  You might need to do “jQuery(window).load” instead.  If you are not using jQuery, there are alternate methods using plain JavaScript, but I have not tested them.  Please leave a comment if you’ve tried any!

Now, when you visit that page from one of your other domains, the ugly query string should disappear after the page finishes loading!!!


Let me say that this technique is still experimental and not endorsed by Google by any means. Before trying this on a production site, do offline experiments!!

Google’s tracking code is asynchronous, which means that there could be timing issues where this code removes the query string variable before Google is done looking at it.  But, it has worked fine in my experiments.  I’ll post any new findings I have.

If you try this, let me know how it works for you!! – Brian


Please Leave a Question or Comment

Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments
5 years ago

3 years from the last comment, but this still does the trick! Thank you so much, Brain, you saved me a lot of headache with this. Code tested with PHP 7.2 and jQuery.

8 years ago

Great article, couldn’t find a solution to this elsewhere. I know you didn’t recommend editing the url yourself, but I found using the following code with regex a little easier than including another file, and I think it should be relatively foolproof:

$(window).load(function() {
currentURL = window.location.href;
newURL = currentURL.replace(/&_ga(\=[^&]*)?(?=&|$)|_ga(\=[^&]*)?(&|$)/,””);
if(newURL.substr(-1) === “?”){newURL = newURL.slice(0, – 1)}
history.replaceState(“state”, “”, newURL);

I *think* this will only fail if there’s ‘_ga’ at the end of a different query string or value.


Mohammed shamil
Mohammed shamil
8 years ago
Reply to  Alfie

Hi Alfie,
I am a newbie when it comes to web hosting/programming and i have similar issue where the ugly string variable pops up due to cross level domain tracking. Could you tell me where exactly should i upload the code you posted onto the server and in which format?.


Nicolas Colombres
8 years ago

Quick arse article. Thanks Brian for this insights ;)