Contents

Cloudflare Zaraz - Tool Triggering based on Cookie Consent

I’ve previous been using Google Tag Manager (GTM) for additional content; Google Analytics, Google AdSense, Facebook Pixel, etc.. on another website. All additional content is, of course, controlled by cookie consent (in my case, using OneTrust) and set / agreed to by the user when they visit the site.

The following notes detail the initial stages of moving the work of the GTM ‘container’ over to the Cloudflare’s Zaraz service, which works in a similar way to GTM but provides site speed and security benefits as the work happens in the cloud rather than on the user’s / client device.

Development setup

My journey involved a little trial and error as it’s still early days for the service and its documentation. However, I found the use of the ‘Zaraz HTTP Request’ tool to be a major help as it allows an HTTP Request to be triggered and thus (if you have a way to monitor that request’s endpoint) provide confidence that your ideas are working (or not!). I made a previous post about it, so if you’ve not read it, check out Cloudflare Zaraz - Using the HTTP Request Tool for Trigger setup / debug. The big takeaway from that was the ability to contain results (good or bad) to my development site, which I’m able to run up on a sub domain away from the production version! You’ll see that nugget throughout this post referenced as a ’trigger’ named NOT _dev which appears in the ‘Blocking Triggers’ of all my development work i.e. the trigger will be BLOCKED if the calling page doesn’t belong to my dev site. The instructions to create it are detailed in the other post so I’ll not repeat that here. Just thought I’d mention why it’s there - of course, once we’re finished with dev it’s simply a case of removing those NOT _dev blocks to allow use in prod 🤓.

Triggers (Rules)

In order to get the ball rolling, we first need a way to ’trigger’ Zaraz’s ’tools’; the tools will be setup once the triggers are in place.

Task #1 - supply the cookie control code. I don’t currently include this in the regular site code as I use one of two versions (as you’ll see, below). In order to know which version of the code to add to the page, I check if the user has visited the site before. If they have, then the cookies recording their previous cookie consent will be available (and used). If they’re a new visitor, we need them to consent to / choose cookies etc. Rather than code that logic, I’m letting Zaraz handle it 🤓

At this point I came across a …

Bug
Potentially a Trigger regex bug: reported here on the #zaraz discord channel .+ is NOT enough to match for a non-empty string (for some reason 🤯). Use .{2,} to successfully match for a non-empty string, i.e. there appears to be an extra character included somewhere before the regex code gets to see it 🤔.

… so, that’s why .{2,} is used below to check for a non-empty value!

Anyway, on with the setup.

In the case of OneTrust, it creates two cookies that are, of course, ‘always allowed’ as they are directly responsible for the site’s function:

  1. OptanonConsent: which initially contains the default cookie group choices.
  2. OptanonAlertBoxClosed: which simply contains a timestamp once the user closes the alert box after confirming their cookie choices.

The OneTrust code then emits a few different events that are available to be used as hooks. The one I use to replace the regular Pageview event is OneTrustLoaded as that now ensures we have our cookie control, well, loaded.

Task #2 - based on the cookie control code and the groups it provides for types of cookies, plus the events fired … we can ALLOW/BLOCK tools being triggered if they are dependant on a cookie group setting. The following triggers provide that control.

OneTrust ‘Triggers’ (rules)

  • Name: Cookie - IS NOT PRESENT - OptanonConsent
    • Match rule {{ system.cookies.OptanonConsent }} Not matches regex .{2,}
  • Name: Cookie - IS NOT PRESENT - OptanonAlertBoxClosed

    • Match rule {{ system.cookies.OptanonAlertBoxClosed }} Not matches regex .*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*
  • Name: Optanon Cookies PRESENT

    • Match rule {{ system.cookies.OptanonConsent }} Matches regex .{2,}
    • AND
    • Match rule {{ system.cookies.OptanonAlertBoxClosed }} Matches regex .*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*
  • Name: C0001 Disabled

    • Match rule {{ client.OnetrustActiveGroups }} Not matches regex .*C0001.*
  • Name: C0002 Disabled

    • Match rule {{ client.OnetrustActiveGroups }} Not matches regex .*C0002.*
  • Name: C0003 Disabled

    • Match rule {{ client.OnetrustActiveGroups }} Not matches regex .*C0003.*
  • Name: C0004 Disabled

    • Match rule {{ client.OnetrustActiveGroups }} Not matches regex .*C0004.*
  • Name: C0005 Disabled

    • Match rule {{ client.OnetrustActiveGroups }} Not matches regex .*C0005.*
  • Name: OneTrustLoaded

    • Match rule {{ client.__zarazTrack }} Equals OneTrustLoaded

Tools

Under control of our triggers we’re now able to hook up some tools. Cloudflare Zaraz has a bunch of preset tools but before setting those up I’ve created a set of custom tools - all HTTP Requests - to provide confidence that the trigger conditions work. You can see the conditions that fire or block (allow or deny) the triggering of tools in the details below.

Initially though, I’ve implemented those two versions of the OneTrust initialisation code.

This will supply the code for the first invocation of the Cookie Consent, i.e. for new visitors to the site. It should only trigger if either the OptanonConsent and/or OptanonAlertBoxClosed cookies are missing i.e. It’s BLOCKED if they are both present. The code here is differentiated in case we want to do something on the initial Cookie Consent setup. Here, it’s simply adding a pleasant “1 second delay” before the user is shown the inevitable cookie consent overlay.

  • Tool: Custom HTML

  • Action:

    • Name: Cookie Consent Init

    • Firing Triggers: Pageview

    • Blocking Triggers:

      • NOT _dev
      • Optanon Cookies PRESENT
    • HTML Code:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      <script>
      (function() {
      try {
          window.setTimeout(
          function(){
              var el = document.createElement('script');
              el.setAttribute('async', '');
              el.setAttribute('src', '/plugins/oneTrust/scripttemplates/otSDKStub.js');
              el.setAttribute('data-domain-script', '4fa46b09-a14e-4a11-2e6f-1e4e58d9afd4');
              document.body.appendChild(el);
              // console.log("Cookie Consent Init");
          }, 1000 );
          } catch (err) {
              // console.log(err);
          }
      })();
      </script>
      <script>
      function OptanonWrapper() {}
      </script>
      

This will supply the code for the regular invocation of the Cookie Consent. We need this version to trigger if the init version has previously succeeded in generating both the required cookies. It’s BLOCKED if either of the cookies are missing - and the init version can then do its thing instead.

  • Tool: Custom HTML

  • Action:

    • Name: Cookie Consent

    • Firing Triggers: Pageview

    • Blocking Triggers:

      • NOT _dev
      • Cookie - IS NOT PRESENT - OptanonConsent
      • Cookie - IS NOT PRESENT - OptanonAlertBoxClosed
    • HTML Code:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      
      <script>
      (function() {
      var el = document.createElement('script');
      el.setAttribute('async', '');
      el.setAttribute('src', '/plugins/oneTrust/scripttemplates/otSDKStub.js');
      el.setAttribute('data-domain-script', '4fa46b09-a14e-4a11-2e6f-1e4e58d9afd4');
      document.body.appendChild(el);
      // console.log("Cookie Consent");
      })();
      </script>
      <script>
      function OptanonWrapper() {}
      </script>
      

HTTP Request - Test Trigger C0001 Enabled

We’ll use this and the following “Test Triggers” simply to verify our triggering logic for content that’s allowed by the C0001 cookie group (i.e. Essential / Strictly necessary cookies)

  • Tool: HTTP Request

  • Action:

    • Name: Test Trigger C0001 Enabled
    • Firing Triggers: OneTrustLoaded
    • Blocking Triggers:
      • NOT _dev
      • C0001 Disabled
    • Endpoint: use a suitable test endpoint (I’m using pipedream.com here), for example:
      • https://eoe166w3tkn8wqv.m.pipedream.net/C0001-Trigger
    • HTTP Method: POST JSON
    • Send all System and Client data: Enable

So, the only time this endpoint will get a hit is (in this example)

  • A OneTrustLoaded event is received (i.e. a page is viewed)
  • AND
  • NONE of the following conditions are true
    • The page does not belong to our dev site
    • The C0001 OnetrustActive group has not been set / agreed to (i.e. it’s disabled)

Please excuse the double negatives, but the Cloudflare Zaraz config doesn’t currently make that easy 🙄. That may change as at the time of writing it’s still in beta 🤓

HTTP Request - Test Trigger C0002 Enabled

  • Tool: HTTP Request

  • Action:

    • Name: Test Trigger C0002 Enabled
    • Firing Triggers: OneTrustLoaded
    • Blocking Triggers:
      • NOT _dev
      • C0002 Disabled
    • Endpoint: use a suitable test endpoint, for example:
      • https://eoe166w3tkn8wqv.m.pipedream.net/C0002-Trigger
    • HTTP Method: POST JSON
    • Send all System and Client data: Enable

HTTP Request - Test Trigger C0003 Enabled

  • Tool: HTTP Request

  • Action:

    • Name: Test Trigger C0003 Enabled
    • Firing Triggers: OneTrustLoaded
    • Blocking Triggers:
      • NOT _dev
      • C0003 Disabled
    • Endpoint: use a suitable test endpoint, for example:
      • https://eoe166w3tkn8wqv.m.pipedream.net/C0003-Trigger
    • HTTP Method: POST JSON
    • Send all System and Client data: Enable

HTTP Request - Test Trigger C0004 Enabled

  • Tool: HTTP Request

  • Action:

    • Name: Test Trigger C0004 Enabled
    • Firing Triggers: OneTrustLoaded
    • Blocking Triggers:
      • NOT _dev
      • C0004 Disabled
    • Endpoint: use a suitable test endpoint, for example:
      • https://eoe166w3tkn8wqv.m.pipedream.net/C0004-Trigger
    • HTTP Method: POST JSON
    • Send all System and Client data: Enable

Usage

Based on the above settings we can now watch our HTTP Request endpoints and see them hit or not depending on the cookie settings we set for our site’s page requests.

Once we’re happy with the test HTTP Request triggers, we can port those same firing / blocking conditions to ‘real’ tools, such as GA4, Google AdSense, various pixels, etc, and be confident that they’re only being instantiated IF the client has allowed them 😎

Annex

  • C0001 Strictly necessary cookies (Essential - allow the website to work)
  • C0002 Statistics cookies (Performance - how the website is used / third-party analytics)
  • C0003 Preferences cookies (Functional - simply store your site preferences)
  • C0004 Marketing cookies (Targeting - track your activity online / third-party)

References