One of the recurring problems in migrating to Google Tag Manager is how to make the transition as smooth as possible. Usually it requires that we agree with the developers on a time when the old code is removed, and at that moment we need to make sure the GTM tags point to the right UA code. This is, of course, only one use case for migrations, as some people do the entire migration in a staging environment, and some just don’t care if they lose a little bit of data along the way.

However, the most seamless way to do the transition is to automate it. Make GTM somehow check for the existence of on-page GA, and if it finds it, the tags will not fire. As soon as the on-page GA is no longer found, GTM can go rampart.

This is a recurring discussion in our Google+ GTM Community, but this time it began in Twitter. Here’s the gist of it:

In this post, I wanted to take a shot at what my good friend Peter is looking for, with what I think is the best way to do this.

Now, there are a number of ways you could go about checking for on-page GA, including:

  • Check for existence of GA cookies

    • Very unreliable as cookies persist after removing on-page code, and if migration is to the same version of GA, the cookies are the same
  • Serialize page template, and regex match for script loaders or calls to the tracking objects

    • Crazy solution (that I just came up with), and isn’t very reliable. Fails completely if scripts are loaded in external JS files
  • Ask developers to add dataLayer.push() that tells the status of on-page GA

    • By far the most reliable solution out there, but the reason most people are looking for a solution like this is to minimize developer intervention
  • Check for existence of ga or _gaq objects

    • Works well if you can bear the wait for the tracking library to load AND execute, as the objects are created in the library code. On some sites, this might be a too long wait, especially if the library is loaded asynchronously. Also, you can rename the ga object, so you’d need to identify it first.

But I want to show you what I think is the best way to do it. Feel free to disagree, and I find myself disagreeing with me as well, especially on a complex site! In my opinion, the following solution does the check as early as possible, and it’s as reliable as it can be, even though there are some caveats.

Solution: look for the <script> elements

This solution looks for the existence of <script> elements, where either ga.js, analytics.js, or dc.js are loaded.

I think it’s pretty reliable, as if the script element is injected in proper form, it means that the libraries have loaded or have begun to load.

Here’s the Custom JavaScript Macro for this. It returns “true” if it finds any of the libraries you specify.

function() {
  var scripts = document.getElementsByTagName('script'),
      ga = true, // set to false if you don't want to check for ga.js
      ua = true, // set to false if you don't want to check for analytics.js
      dc = false, // set to false if you don't want to check for dc.js
      i = len = 0;
  if (ga || ua || dc) {
    for (i, len = scripts.length; i < len; i += 1) {
      if (ga && /www\.google-analytics\.com\/ga\.js/.test(scripts[i].src)) {
        return true;
      if (ua && /www\.google-analytics\.com\/analytics\.js/.test(scripts[i].src)) {
        return true;
      if (dc && /stats\.g\.doubleclick\.net\/dc\.js/.test(scripts[i].src)) {
        return true;
  return false;

So now you can add {{check for scripts}} equals true as a blocking trigger in your GTM GA tags.

There are some shortcomings to this method. For example, the page can load the libraries but not do any tracking. This can be true for legacy setups, where only the _gaq.push() commands have been removed, but the library load remains. So naturally, you will have to audit the site before implementing this solution.

I tested this out a couple of times on my own blog, using it as the blocking rule of my pageview tag, and it seemed to work fine.

Naturally, since the ga.js snippet is added to the end of the page (stupid, synchronous script), you might need to wait for gtm.dom before checking the blocking rule, especially on heavy pages.


Remember, this simple solution is only for the transition from on-page GA to GTM. The only thing it does is prevent you from having to sit in front of a computer, constantly refreshing the page to see if the on-page code has been removed.

A migration requires vigilance, so even if you manage to automate the transition with this solution, you will still need to be alert throughout the process. But you knew this already (didn’t mean to sound condescending).