cookieowl

Chapter V of IX

How to block Google Analytics until consent

Hiding a banner is easy. Keeping gtag.js from loading until the visitor says yes takes three small changes, and you can prove it in the Network panel.

By Updated 7 min read

The problem with the standard snippet

The Google Analytics 4 snippet that Google gives you has two parts: an external script, gtag/js?id=G-XXXX, and an inline block that calls gtag('config'). Both run as soon as the browser parses them. By the time your banner paints, gtag.js has been downloaded, the _ga cookie is set and the first page view has been sent. A banner placed on top cannot undo that.

Blocking means the browser never executes those scripts until the visitor accepts analytics. There is a simple, standard way to do it: a script whose type is not a JavaScript type is not executed, and not even downloaded. type="text/plain" turns a script into inert text.

Three changes to your head

First, load cookieowl as the very first script, synchronously. Second, change the type of both parts of the GA snippet to text/plain. Third, add data-cookieowl="analytics" so cookieowl knows which category releases them:

index.html
<head>
  <script src="https://cdn.jsdelivr.net/npm/cookieowl@0.1.0/dist/cookieowl.js"
    data-policy-url="/privacy" data-consent-mode></script>

  <script type="text/plain" data-cookieowl="analytics" async
    src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
  <script type="text/plain" data-cookieowl="analytics">
    window.dataLayer = window.dataLayer || [];
    function gtag() { dataLayer.push(arguments); }
    gtag('js', new Date());
    gtag('config', 'G-XXXXXXX');
  </script>
</head>

When the visitor accepts analytics, cookieowl replaces each inert script with a live copy, in document order. The inline block runs right away; external scripts without async are waited for, so a script that depends on another never runs too early. If the visitor never accepts, nothing is requested from Google.

When you cannot edit the snippet

Plugins, themes and tag managers often inject Google Analytics from their own code. For those, auto mode recognizes the URL of the script when it is added to the page, whether by the HTML parser or by document.createElement, and holds it the same way:

index.html
<script src="https://cdn.jsdelivr.net/npm/cookieowl@0.1.0/dist/cookieowl.js"
  data-consent-mode data-auto-block></script>

Auto mode knows gtag.js, the older analytics.js, Google Tag Manager and Google Ads, along with the Meta Pixel, Microsoft Clarity, Hotjar, LinkedIn Insight, the TikTok Pixel and YouTube and Vimeo embeds. Two honest limits: it must load before the tracker (so keep cookieowl first, synchronous, in the head), and the browser preload scanner may download, but not run, a tracker written directly in the HTML. Explicit text/plain markup avoids even that download.

Verify it in DevTools

Open a private window, open the developer tools on the Network tab and type google in the filter. Load your page. The list must stay empty. In the Application tab, the cookie list must not contain _ga or _ga_ entries. Then click Accept all: gtag/js and a collect request appear, and the cookies are set.

You can also check from the console. Before a choice, cookieowl.accepted('analytics') is false and the GA scripts still have type="text/plain". After accepting, they are replaced and marked with data-cookieowl-done.

Withdrawal: deleting _ga and stopping the script

A script that already ran cannot be unloaded, which is why withdrawal needs two steps. cookieowl erases the cookies it knows for the category (for analytics that includes _ga, _ga_*, _gid and _gat* on your domain and parent domains), and then reloads the page because analytics scripts ran on it. After the reload, the scripts are inert again.

spa.js
cookieowl.init({
  // In a single page app you may prefer to clean up yourself.
  reloadOnWithdraw(categories) {
    if (categories.includes('analytics')) window.location.reload();
  },
});

Questions

Why not just set analytics_storage to denied and load GA anyway?

That is Consent Mode advanced mode. GA then loads before consent and sends cookieless pings to Google. Some authorities consider any request to a third party before consent a problem, so many sites prefer basic mode, where gtag.js does not load at all until consent.

Does blocking GA lose data?

You only lose hits from visitors who did not accept analytics, which you are not allowed to collect with cookies anyway. With Consent Mode in advanced mode Google can model part of the gap, at the cost of loading before consent.

Will GA count the page view after the visitor accepts?

Yes. When analytics is accepted, the gtag scripts run on the same page, so the config call sends the page view right away without a reload.

What about Google Analytics added by a plugin or a tag manager?

Turn on auto mode with data-auto-block. cookieowl recognizes gtag.js, analytics.js and gtm.js by URL and holds them even when another script injects them.

Hoot suggests

Nothing here. Hoot looked under every cookie jar.