Our homepage has a cookie banner. Google Consent Mode v2 is wired into it properly: analytics and advertising storage are denied before the page does anything, granted only after the visitor clicks accept, and the choice is persisted so we do not ask again. We have opened that page and tested that banner more times than we can count. It works.
Then we stopped opening the homepage and started counting pages instead.
Thirty-three pages on this site load Google Analytics. Twenty-eight of them set consent to denied by default. Exactly one of them — the homepage — is able to ask the visitor to change that.
What Consent Mode v2 Actually Does
Consent Mode is a handshake in two parts. Before the tag does any measurement, you declare a default posture. Ours, sitting inline in the <head> of every page, denies everything:
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied',
'wait_for_update': 500
});
The second part is the update call, which fires when a visitor consents. That call is the entire point of the first one. A default-deny with no path to an update is not a privacy feature — it is a measurement outage that happens to be compliant.
It is worth being precise about what "denied" means, because a lot of writing on this gets it wrong. Denied does not mean nothing is sent. GA4 still receives cookieless pings, so you keep seeing traffic in your reports and nothing looks broken. What you lose is the cookie: no reliable stitching of one visitor across pages or sessions, degraded attribution, and conversion modelling that only helps if you feed it enough consented data to model from. The dashboard stays populated. The numbers quietly stop meaning what you think they mean.
Finding One: The Banner Lives on One Page
The banner markup and its two click handlers exist only in index.html. Every blog post, every service page, every app page carries the deny-by-default snippet and no way to lift it.
Read that against how people actually arrive. Search traffic does not land on homepages. It lands on the article that answered the query, or the service page that matched the search. A visitor who arrives from Google onto one of our posts is denied by default and never shown a choice. They read, they leave, and their entire visit is measured cookielessly. If they navigate to the homepage mid-visit the banner finally appears — but a reader of one article has no reason to.
The sharper version of this problem is paid traffic. We run Google Ads pointed at service landing pages, not at the homepage. Those pages deny ad_storage and ad_user_data by default and cannot ask for either. That is ad spend landing on pages structurally incapable of attributing a conversion properly.
Finding Two: Five Pages With No Gate at All
The same count turned up the opposite failure. Five of the thirty-three pages — the most recently added app pages — load GA4 with no consent call whatsoever:
gtag('js', new Date());
gtag('config', 'G-T97BQ3VRV1');
No default, no deny, no update. Those pages fall back to the tag's unrestricted behaviour and set cookies immediately, for every visitor, with no banner anywhere in sight. One site, two opposite defects: twenty-eight pages that refuse to measure and cannot ask permission, and five that measure without asking at all.
Neither of these was a decision. Both are what happens when an analytics snippet is copied by hand into each new page and the copy source drifts over time.
Why This Survives Testing
Two things conspire to hide it.
- You test the homepage. It is the page you open when you want to look at the site. It is also the only page with the banner, so the banner always works when you check.
- Consent persists. Once you have clicked accept, your own browser restores that consent from storage on every page. The site behaves correctly for you, permanently, while behaving incorrectly for every first-time visitor. Clearing storage or opening a private window is the only way to see what they see.
This is the same lesson as our accessibility audit and our audit of hotlinked images: the defects that survive longest are the ones invisible from the seat you normally sit in. The fix is not to look harder. It is to stop looking and start counting.
Audit Yours by Counting, Not Looking
Three commands, run at the root of your site, will tell you in seconds what an afternoon of clicking will not. Compare the numbers:
# How many pages load the tag?
grep -rl "G-XXXXXXXXXX" --include='*.html' . | wc -l
# How many declare a consent default?
grep -rl "consent', 'default'" --include='*.html' . | wc -l
# How many can actually ask the visitor?
grep -rl 'id="cookieBanner"' --include='*.html' . | wc -l
All three should be the same number. If the second is lower than the first, some pages are tracking ungated. If the third is lower than the second, some pages are denying consent with no way to lift it. Ours read 33, 28, and 1.
On a site with a build step or a CMS this class of bug is largely designed out — the snippet lives in one layout and every page inherits it. On a hand-built static site, which this is, nothing enforces that. The remedy is the same either way: one consent implementation, included everywhere, never copied. We are consolidating ours into a single shared include and deleting twenty-eight hand-maintained copies, which also means the next change to it is one edit rather than twenty-eight.
What to Take From This
If you have a cookie banner and a Consent Mode setup, the question is not whether it works. It is on how many pages it works, and whether that number matches the number of pages carrying your tag. Check it from a private window, on a page that is not your homepage, arriving the way a stranger would.
And be suspicious of an analytics setup that has never looked broken. Consent Mode fails quietly by design: the reports stay full, the graphs keep their shape, and the only visible symptom is numbers that are subtly, unfalsifiably wrong. We do not know how much of our own traffic was measured without cookies before we counted, and we never will. That is precisely the problem with this kind of bug — it destroys the evidence of its own existence.