Cookie compliant Google Optimize

Posted on Sun 02 February 2020 in 2020

EU and UK rules mean that we must ask users for consent to place cookies on their machine, or to use LocalStorage and similar technology. This post briefly describes the implications for marketing and analytics tags in Google Tag Manager (GTM), and then the more interesting case of Google Optimize and other A/B testing tools which shouldn't really be run through GTM.

Google Tag Manager

For most marketing and analytics tags in GTM, this is a manageable situation, if tedious to administer. Wherever you currently have a tag firing on page views, you want instead to have it fire on a custom dataLayer event. Fire the dataLayer event in either of the following cases:

  • A user clicks on the "accept cookies" button on the consent banner you're using. When they do this, you want to save their consent status to a cookie, and fire the dataLayer event.
  • A user visits a page and the consent cookie is already present.

Now you have a little setup to do in GTM:

  1. Create a Variable of type First Party Cookie. This should read the value of the cookie you're using to store consent preferences. Have undefined and null default to false.
  2. Create a trigger of type Custom Event. Call it Cookie Consent Granted or something similar. Have it match the dataLayer event name you defined earlier.
  3. Create a trigger of type Custom Event. Call it Cookie Consent Not Granted or similar. Have it match all event names: .* with regex enabled. Filter on those events so that this trigger only fires when the First Party Cookie variable defined in step 1 is equal to false.


  1. Wherever you are currently working from a page view trigger, use the Cookie Consent Granted event.
  2. Wherever you're using triggers that fire later—form submissions, clicks, etc—add Cookie Consent Not Granted as an exception trigger.

Google Optimize

If you're using Google Optimize, it's recommended not to add it to GTM, because it's too slow. You have to add it directly to the page. The trouble here is that:

  • You have to add Google Analytics to the page before Google Optimize.
  • Google Analytics will create a clientId to represent this user. Usually that's stored in a cookie.
  • But you're not allowed to set cookies until the consent is given.
  • Google Optimize has to start running very quickly after the page loads, or all users will see the control.
  • Google Optimize has to finish running quickly after that, otherwise there'll be an unacceptable delay and users will see a white screen for up to 4 seconds.
  • If consent is given, you don't want to have lost track of the clientId (which represents this unique user). You need to record that they've seen this variant.


I'll assume here that you:

  • have a JavaScript function hasConsent defined somewhere, returning true if you know the user has already consented to cookies, and false otherwise.
  • fire an event called acceptsConsent whenever the user actually accepts cookies by clicking "Accept" on a banner or popup.

The implementation details of hasConsent and acceptsConsent could vary from site to site. But the way Google Optimize should work with them is the same in all cases:

if(hasConsent()) {
        ga('create', 'UA-XXXXXXXX-XX');
        ga('require', 'GTM-XXXXXXX');
        ga('send', 'pageview');
    } else {
        window['ga-disable-UA-XXXXXXXX-XX'] = true;
        ga('create', 'UA-XXXXXXXX-XX', { storage: 'none' });
        ga('require', 'GTM-XXXXXXX');

        window.addEventListener("acceptsConsent", function() {
        // Send the initial pageview
            window['ga-disable-UA-XXXXXXXX-XX'] = false;
            ga('send', 'pageview');

        // Get the clientId so we don't lose track of it
            var clientId = null;
            ga(function(tracker) { clientId = tracker.get('clientId'); });

        // Remove the cookieless tracker

        // Create a new one, storing the original clientId in a cookie
            ga('create', 'UA-XXXXXXXX-XX', { clientId: clientId });
            ga('require', 'GTM-XXXXXXX');

Replace the UA-XXXXXXXX-XX strings with your Google Analytics property ID and the GTM-XXXXXXX strings with your Google Optimize container ID.

If consent has been granted already, we've nothing to worry about: create the GA tracker, require Optimize, and send the pageview.

If consent hasn't been granted, we still create the GA tracker and require Optimize. We must pass {storage: 'none'} to the tracker creation step to avoid setting cookies. We should also set the ga-disable- property on the window before we create the tracker. This will prevent Google Analytics from sending any user data over to Google for the time being.

We add an event listener so that as soon as the user consents, we send a pageview. We have to toggle ga-disable- back off again, otherwise the ga('send', 'pageview') line will have no effect.

In the pageview event, we can expect the original clientId to be present, and this will be recorded in Optimize as well, because we did require that plugin.

However, at this step we have a problem: it's very important, since we've now sent that pageview event and Optimize is tracking that user, to persist the clientId in the usual GA cookies. If we don't do this, any subsequent page view would generate a new clientId—Google Optimize would not know that this was the same user, and the test is ruined.

There's no way to set the storage property on a tracker object once it has been created. We have to create a new one, but pass in the clientId from the original tracker.

We remove the original tracker, since we don't want more than one on the page. We have to require Google Optimize again, since we have thrown away and re-created the tracker object.

Now, if you have subsequent event calls on the page—like form submissions which might relate to a Google Analytics goal—these will be captured correctly. The clientId included in the event hits will match that of the original pageview. And the clientId is stored in a cookie so will persist as the user moves around your site.