Skip to main content
Skip table of contents

Proxing Google Tag Manager

This guidance is for publishers and tag implementers who have access to Cloudflare DNS and Worker Services. There is likely to be some small additional cost to running the proxy. You can explore those costs by visiting this link.

Do note that this guidance is for general information only and is not a requirement for running the Anonymised Marketing tag. Anonymised is not an agent nor an affiliate of Cloudflare.

Why Proxy Google Tag Manager (GTM)

There are 3 main reasons for exploring running GTM through a proxy:

  1. Speed. Using the Cloudflare CDN can have considerable performance benefits for sites running GTM.

  2. Security. Hosting a subdomain to proxy GTM makes it less prone to cross-site scripting attacks.

  3. Reliability. Google Tag Manager is prone to Adblockers and other privacy initiatives stopping the service and the scripts that it manages from executing.

How to configure Cloudflare to serve as the GTM proxy

  1. Go to your Cloudflare account and set up a new worker.

  2. Write a script to respond to another site and use a cache API. In the example below, we receive the call to the proxy URL, check the cache and respond appropriately:

    CODE
    // Make sure you update this following line for your GTM container URL and ID
    const GTM_URL = 'https://www.googletagmanager.com/gtm.js?id={GTM CONTAINER ID}'; 
    const CACHE_EXPIRY = 30; // cache timeout in seconds
    
    // Listen to any incoming requests and apply our function
    addEventListener('fetch', function(event) {
      try {
        event.respondWith(handleRequest(event))
      } catch(e) {
        console.error(e)
      }
    })
    
    async function handleRequest(event) {
      const request = event.request
    
      // Only GET requests work with this proxy.
      if (request.method !== 'GET') return MethodNotAllowed(request)
    
      // https://developers.cloudflare.com/workers/examples/cache-api
      const cacheURL = new URL(request.url);
    
      const cacheKey = new Request(cacheURL.toString(), request);
      const cache = caches.default
    
      let response = await cache.match(cacheKey);
    
      if (!response) {
        // If not in cache, get it from origin
        response = await fetch(GTM_URL);
    
        // Must use Response constructor to inherit all of response's fields
        response = new Response(response.body, response)
    
        response.headers.append("Cache-Control", `s-maxage=${CACHE_EXPIRY}`)
    
        // Store the fetched response as cacheKey
        // Use waitUntil so you can return the response without blocking on
        // writing to cache
        event.waitUntil(cache.put(cacheKey, response.clone()))
      }
    
      return response
    }
    
    // Use the following when the request is not a valid GET request
    function MethodNotAllowed(request) {
      return new Response(`Method ${request.method} not allowed.`, {
        status: 405,
        headers: {
          'Allow': 'GET'
        }
      })
    }

Note: This is example code only, please conduct your checks before deploying and be sure to check the cache timeout to your preference.

  1. Set up a subdomain to host the worker using Cloudflare DNS:

    1. In your Cloudflare zone file, create an entry similar to the following:

      Example of the required DNS entry

      Example of the required DNS entry

    2. Note that the Cloudflare Worker will intercept the request before it reaches any IP address, so you may use a dummy IP address to enable the saving of the DNS entry.

  2. Map the Worker to the subdomain by navigating to the Workers Routes, in your Cloudflare dashboard, and adding a new route, similar to the following:

    Mapping the Worker to the subdomain

    Mapping the Worker to the subdomain

  3. Finally, alter the GTM snippet on your site to reference the proxy URL with that of Googles. This means replacing the line of script googletagmanager.com with your subdomain as created above. See example below:

    CODE
    <!-- Google Tag Manager -->
    <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://gtm.your-domain.com';f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXX');</script>
    <!-- End Google Tag Manager -->

Note that in the example we have also removed the Container ID, as this is not necessary on the page, and is set in the Worker Script, thus improving site security.

As with all customisation, we strongly suggest that adequate testing is performed before making such critical changes and where possible a sandbox environment should be used.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.