Appearance
Google Consent Mode v2
Google Consent Mode lets Google's own tags — GA4, Google Ads, Floodlight, tags fired through Google Tag Manager — adjust to the visitor's consent. Since March 2024 it is required for remarketing and conversion modelling on EEA/UK traffic. CookieWave sends the signals.
Set up
- In Banner Designer → Consent, turn on Enable Google Consent Mode v2.
- Install your Google tags (gtag.js or GTM) normally, in the
<head>, after the CookieWave snippet.
The banner sets consent defaults before your tags load, then updates them on the visitor's choice:
js
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
functionality_storage: 'denied',
personalization_storage: 'denied',
security_storage: 'granted',
wait_for_update: 500,
})
// on the visitor's choice:
gtag('consent', 'update', { /* the same keys, granted where consented */ })The default does not wait for anything
The denied default is not fetched. It is prepended to script.js at the CDN edge from your site's own settings, so the first line of the file your visitors download is:
js
window.__cwBoot={"gcm":true,"dl":"","wfu":500,"adr":true,"up":true};The banner reads that in the same tick the script parses — before your Google tag has finished loading — and declares the denied default from it. Nothing about the visitor is in that line: it only ever says "denied", and the real config decides the rest a moment later.
This matters most on the install Google's own instructions produce, with gtag.js loaded directly and no default written in the page. That tag sends its first hit around 78ms in and stores a cookie with it; a CMP that has to fetch its configuration first cannot answer in time. Now it does not have to.
Sites with Consent Mode switched off are never sent Google signals from it — declaring them for a site that asked for none would be inventing configuration. The line itself may still be there, because the same mechanism now carries the Microsoft toggles and your script-blocking rules; it simply says nothing about Google.
How categories map to signals
| Signal | Granted when the visitor accepts |
|---|---|
ad_storage, ad_user_data | marketing |
ad_personalization | marketing |
analytics_storage | analytics |
functionality_storage, personalization_storage | preferences |
security_storage | always granted |
On a TCF site
There are no categories to map from, so the signals come from TCF purposes. Google publishes its own table for the three advertising signals, and CookieWave applies that table rather than a reading of its own — two mappings of the same purposes that disagree would be a bug invisible from either side:
| Signal | Granted when the visitor allows | Source |
|---|---|---|
ad_storage | purpose 1 | Google's table |
ad_user_data | purposes 1 and 7 | Google's table |
ad_personalization | purposes 3 and 4 | Google's table |
analytics_storage | purpose 1, and either 8 or 9 | ours |
functionality_storage | purpose 1 | ours |
personalization_storage | purposes 5 and 6 | ours |
security_storage | always granted | Consent Mode convention |
Google derives nothing from TCF for the last four, so those are CookieWave's reading of which purposes the signal describes. Purpose 1 appears in most of them because it is what governs device storage at all: without it there is nothing to store, whatever else was allowed.
"Allows" means consent given, or — for a purpose you declared under legitimate interest — not objected to.
Advanced parameters
The banner also sets Google's advanced parameters, at their recommended defaults:
wait_for_update: 500— gives tags a moment to receive the update before acting on the default.ads_data_redaction: true— redacts ad identifiers whilead_storageis denied.url_passthrough: true— passes ad-click IDs through URLs so conversions can still be measured cookielessly.
These are safe for almost everyone and need no configuration. If you must change them, they can be overridden in your published config; ask support.
Google Tag Manager
GTM honours Consent Mode natively: with the toggle on, GTM's built-in consent checks gate your tags' storage without any custom trigger. You do not need a Custom Event trigger for Google tags — that is only needed for non-Google tags, covered under Consent events.
Put the CookieWave snippet before the GTM snippet
Not just in the <head> — above it.
GTM's snippet does not load GTM. It pushes gtm.start and injects an async gtm.js, which then arrives whenever the network delivers it. If the CookieWave snippet sits below, its own request may still be in flight when gtm.js runs, and your tags read a data layer with no consent default in it.
What makes that expensive is not precedence — a later default does replace an earlier one — but that a tag which has already sent its hit is never revisited. The hit is gone, sent under no consent state at all. So the cost of arriving late is measured in tags that already fired, and no correction afterwards recovers them.
Putting CookieWave first removes the race rather than relying on winning it.
html
<head>
<!-- 1. CookieWave -->
<script id="cookiewave" src="https://cdn.cookiewave.com/cli/YOUR-SITE-ID/script.js"></script>
<!-- 2. Google Tag Manager -->
<script>(function(w,d,s,l,i){ /* ... */ })(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
</head>Do not load CookieWave from inside GTM
It is a tempting tidy-up and it breaks three things at once:
- The default arrives too late. By the time a GTM tag runs, GTM is already running and may already have fired other tags.
- Ad blockers take the banner with them. Many block
googletagmanager.com. If that is what loads your CMP, those visitors get no banner at all — the site silently stops asking for consent. - Script blocking stops working. CookieWave holds back scripts marked
type="text/plain" data-category="..."at page load. Arriving later, it has already missed them.
GTM is for your tags. The CMP has to come before it.
If the banner fails to load
With Consent Mode on, your Google tags load immediately and wait for the default. That is the design — it is how a visitor who refuses still produces a signal Google can model from. But it means the denied state depends on the CookieWave snippet arriving.
Usually it does, and it no longer has to wait for its configuration to get there: the default rides on script.js itself, as described above. What it cannot survive is the script not arriving — a CDN incident, an ad blocker, a snippet on a domain that is not registered for the site. The default is in that file, so if the file is blocked the default is blocked with it, and every tag in your container behaves as though consent had been given.
That is the one case worth a belt to your braces. If you want one, declare the default in your own HTML, above both snippets:
html
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('set', 'ads_data_redaction', true);
gtag('set', 'url_passthrough', true);
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
functionality_storage: 'denied',
personalization_storage: 'denied',
security_storage: 'granted',
wait_for_update: 500
});
</script>Three things to get right:
- List every signal.
gtag('consent', 'update', ...)only touches the keys it is handed, so a key you omit keeps whatever the default said — silently, with no error anywhere. - Match the values above. CookieWave pushes its own default too, and the later default for a signal is the one that counts — measured against a real Google tag:
default grantedfollowed bydefault deniedleaves the signal denied, not granted. So two defaults that disagree do not settle by who was first; the last one wins, whichever that turns out to be. Make them agree. - Region-specific defaults still apply. A default scoped to a region takes precedence over one without, so this global floor does not override region-targeted rules.
This is optional, and it is a narrower recommendation than it used to be: it buys nothing against slowness, only against the script never arriving at all. Most CMP installations rely on the CMP alone, and Google documents both approaches.
Or block GTM instead
The other way to be safe is to not load GTM until consent:
html
<script type="text/plain" data-category="analytics">
(function(w,d,s,l,i){ /* the GTM snippet */ })(window,document,'script','dataLayer','GTM-XXXXXXX');
</script>For tags you cannot edit — a tag manager's own output — see Blocking scripts, which holds them back by URL.
Pick one. Blocking GTM and enabling Consent Mode loses most of what Consent Mode is for: until someone consents, GTM does not exist, so Google never hears the denied default either — no signal for visitors who refuse, and nothing to model from.
| Blocked GTM | GTM + Consent Mode | |
|---|---|---|
| Before consent | does not load | loads, tags wait in denied state |
| Signal when a visitor refuses | none | gcs=G100 |
| Conversion modelling | no | yes |
Use Consent Mode unless you have a reason not to. If you ever turn it off, put the blocking attributes back — otherwise GTM will load and set cookies before anyone has agreed to anything.
If your data layer is not called dataLayer
Google Tag Manager and gtag both let you rename the data layer, with an l= parameter on the loader:
html
<script src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX&l=myLayer"></script>If yours is renamed, set the same name under Settings → General → Data layer name. Leave it empty otherwise.
This is worth two minutes of checking because of how it fails. Consent signals go into the array you name here; your tags read the array they were told about. If the two differ, every default and update lands somewhere nothing reads — and nothing complains. The banner works, visitors' choices are recorded, and Google simply never hears any of it.
What you would see instead
In Google's own reporting, a consent rate of 0% with every signal denied. That is the same symptom as a banner that is genuinely never accepted, which is why it can go unnoticed for months.
The banner cannot work this out for itself. It is meant to load before your Google tag — that is the whole point of the install order — so at the moment it needs the name, the tag is not on the page yet to be asked. What it does do is check a few seconds later and write a console warning if the two disagree:
CookieWave: this page loads Google Tag Manager with data layer "myLayer",
but consent signals are being pushed to "dataLayer". Consent Mode will have
no effect until they match.Test it on your machine
/cli/{siteId}/config.json answers 403 for an origin that is not one of the site's registered domains, and without a config the banner never starts. On localhost that means no banner, no defaults, and your Google tags running unrestricted — which looks like Consent Mode is broken when it is simply not running.
Add localhost under Settings → General → Subdomains and it will load.
Verify
Load a page and inspect the data layer in the console:
js
window.dataLayer.filter((e) => e[0] === 'consent')
// → the 'default' entry, then an 'update' entry after a choiceThe default entry must come before anything from GTM. If you see an update but no default, the CookieWave snippet loaded after your tags — check the order in your <head>.