How to safely migrate a tag to GTM

How to safely migrate a tag to GTM

The standard way to migrate a tag to Google Tag Manager is probably something like this:

Step 1: We have the website with the tag (in this case a GA4 tag) in the website source code.

Step 2: The GTM container code gets added to the website. At the moment, it might not have any tags in it.

Step 3: The GTM container is updated to replicate the tag that was there on the website. For example, here we might create the GA4 tag again. It’s not published because the original is still on the website, so publishing both would double up the tracking.

Step 4: Now, once we’ve tested everything, we need to remove the original hard-coded tag from the website and publish GTM at the same time (or as close as possible).

In our experience though, it’s steps 2 and 4 which are the weakest link. In step 2, we’ve often had developers misunderstand the instructions and replace the original tag with the GTM code (but until it’s configured this would break all tracking). And for step 4, it’s often hard to coordinate this being at the same time, especially if one person is doing the code change and one is publishing GTM. So how can we make the process more resilient?

Getting GTM ready before it’s added to the website

There are a number of Chrome extensions that will inject the GTM container of your choice into your page. This means that you can create the container, set up tags, and use one of these extensions to test them, so that by the time the container code is ready to give to the developer, it could just replace the original tag.

If you don’t want to use an extension, you can also add GTM to your browser directly. When you click to install GTM in the interface, you will get a popup with code like this where XXXXXX is your container ID:

<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>

This is essentially just Javascript, so if you copy the code that’s inside the <script> tags (do NOT include them), go to your browser’s developer console and paste the code, which will start up GTM. You can even do this in the GTM preview once the window opens. Just note that if you are testing a multi-page process (eg. a checkout), you will have to do this on each page as you’re simulating the user journey, which can be a pain, but for simpler tests this can be a good alternative to installing a plugin.

Getting GTM to fire a tag only if it’s not already hard-coded

If you follow the steps above, you may not need this, since you might be able to get GTM set up by the time it’s ready to install on the website. But what if it’s already installed and you just need to migrate an additional tag into GTM and don’t trust that the timelines will align?

You can use GTM to only fire a tracking code only if it’s NOT already hard-coded on the page. That way, if you publish it, nothing will happen and it won’t fire the tag. But the moment the developer removes the hard-coded tag from the page, GTM will take over. Note that this will eliminate any data gaps, but once the code has been removed we recommend updating GTM again to fire the tag as you normally would.

We can implement this in GTM because almost all tracking tags add a <script> object to the page, which points to the tool’s main Javascript library. So GTM can fire some Javascript that checks if such a script tag has already been added to the page and if it hasn’t then it can fire the tag itself.

The first step would be to write a CSS selector that only matches the tracking script in question. It can take this form:

script[src*='XXX']

XXX is the actual URL of the tag’s script library. You can get this by checking out the hard-coded tag yourself, but at the time of writing here are the values for the most common tags:

  • GA4/Google Ads: googletagmanager.com/gtag/js?id=XXX (where XXX will be your GA4 or Google Ads measurement ID)
  • Meta Pixel: connect.facebook.net/en_US/fbevents.js
  • LinkedIn Insight Tag: snap.licdn.com/li.lms-analytics/insight.min.js
  • Microsoft Universal Event Tag: bat.bing.com/bat.js
  • Pinterest Pixel: s.pinimg.com/ct/core.js
  • TikTok Pixel: analytics.tiktok.com/i18n/pixel/events.js

To put this together, let’s imagine that we are trying to migrate the Meta Pixel into GTM. The selector for this would be:

script[src*='connect.facebook.net/en_US/fbevents.js']

We can then use this template to fire a custom HTML tag in GTM, which determines if the pixel is present on the page and if not fires an event to which we would attach the GTM tag.

<script>
  /* We delay the test by 1 second since by the time this tag fires in GTM, 
      the hard-coded tag might not be finished attaching the script. You can
      change this value if you need */
  var delay = 1000;
  
  /* This is the selector for the script we are testing for, you can modify it to a
     different value as per the details above. */
  var selector = "script[src*='connect.facebook.net/en_US/fbevents.js']";

  setTimeout(function () {
    var hardCodedScript = document.querySelector(selector);
    if(!hardCodedScript) {
        /* If we've entered this part of the code, we failed to fetch the script, meaning
            the hard-coded tag is not there. We can fire the tag in GTM instead.
            You can modify the event name if you need. */
        window.dataLayer.push("fire-meta-pixel");
    }
  },delay);
</script>

Next, you’ll need to create a custom data layer event in GTM set to fire on the “fire-meta-pixel” event and attach it to your Meta Pixel tag. This will only fire from GTM if/when the developer has removed the Meta Pixel from the website’s code!