Every consent audit we've ever seen a client run tests the same thing: click Accept all, check that GA4 starts firing, confirm gcs=G111 appears on the network, ship it. The CMP works.
But "Accept all" is not where CMPs fail. Clicking accept grants everything — there's no mismatch possible. The failure mode every regulator cares about is the other button: the one the compliance team hopes nobody clicks.
The failure mode nobody tests
When a user clicks Reject all, three things should happen:
- All four Consent Mode v2 signals (
ad_storage,analytics_storage,ad_user_data,ad_personalization) transition or remain atdenied. - Any tracking tag gated by consent stops firing — no new GA4 requests should hit
/g/collectwith a grantedgcs. - The user's choice persists across navigation and future visits (usually via an opt-out cookie or localStorage flag).
In the field, step 1 fails routinely. We've seen CMPs that:
- Fire
gtag('consent', 'update', { ad_storage: 'denied' })on reject — but forget the other 3 signals, leaving them at the defaultgrantedstate from a misconfigured initial call. - Call the update with analytics_storage correctly denied, but flip
ad_storageback tograntedbecause a line of custom CMP code reads "functional cookies" as implying ad consent. - Don't fire
consent updateat all on reject. The banner closes. The user thinks they opted out. Tracking continues with whatever default was set pre-consent — which on many sites is granted.
Why this is a direct GDPR violation
A user who clicks "Reject all" has expressed an explicit, informed refusal of consent under the ePrivacy Directive. If tracking continues after that click, every subsequent data point is collected without a lawful basis. Under GDPR, the fine ceiling is 4% of global annual turnover. Under France's CNIL enforcement priorities (similar regulators exist across EU member states), this is exactly the scenario that triggered the largest consent fines of 2023-2024.
It's also the scenario least likely to be caught during a standard compliance review — because everybody tests accept and nobody tests reject.
How to test it in your browser
The manual test is straightforward but tedious, which is why it's skipped:
- Open a private/incognito window.
- Open DevTools → Network, filter by
collect. - Navigate to the site.
- Wait for the cookie banner.
- Click Reject all.
- In the console, paste:
Object.fromEntries( Object.entries(google_tag_data.ics.entries).map( ([k, v]) => [k, v.default ? 'granted' : 'denied'] ) ) - Every value should be
denied. Anygrantedis a finding. - Navigate to another page on the site. Watch Network. No new GA4 requests should fire with a granted
gcs.
If you manage a portfolio of client sites, running this across all of them is hours of work. Which is why it doesn't get done.
How our audit automates it
Check My Tracking's audit engine runs the reject-flow test automatically as part of its CP_03 check. Under the hood it:
- Runs the primary scan (navigate, accept CMP, verify accept behavior) — this is what every audit does.
- Launches a second isolated browser context from a clean state.
- Navigates to the same URL, waits for the CMP, looks up the correct reject selector in our registry (we maintain stable selectors for Cookiebot, OneTrust, CookieYes, Didomi, Iubenda, Usercentrics v3, Klaro, Borlabs, Complianz, Osano, and consentmanager).
- Clicks reject, waits 1.5 seconds for the CMP to propagate the choice to Consent Mode.
- Reads
google_tag_data.ics.entriesand captures any GA4 request that fires after the click. - If
ad_storageoranalytics_storageis still granted after reject, CP_03 fails regardless of how the accept path went.
The accept-path still matters (a broken accept is a product issue — no attribution). But the reject-path is the regulatory risk. Testing both is the only way to know your CMP actually does what you paid it for.
What to do if your reject test fails
If our audit surfaces a reject-flow failure on your site, the fix depends on where the break is:
- Missing consent update call.Inspect your CMP's reject callback. It should invoke
gtag('consent', 'update', {…})with all 4 signals set to denied. Major CMPs do this out of the box — custom integrations frequently forget. - Partial signal update.Some integrations map the CMP's category model ("necessary", "marketing", "analytics") to a partial set of Consent Mode signals, leaving others untouched from the default. Add explicit mappings for ad_user_data and ad_personalization.
- Default-granted bootstrap. Your initial
gtag('consent', 'default', …)call usesgrantedfor one or more signals. The reject update runs correctly but starts from a wrong baseline. Fix the defaults first.
Reject is the test that matters
Reject-all validation is the single highest-leverage consent compliance check you can run — precisely because nobody else is running it. Manual testing is tedious, automated audit tools don't bother. If your site passes our free scan's CP_03 reject-flow check, you're ahead of roughly 70% of production consent implementations we've seen.