The Consent Banner Said Yes. The Tags Heard No.
A WooCommerce brand's visitors were accepting the cookie banner, and the Google beacons still fired with consent denied. The free tier of the consent platform never told Google about the yes. The ad account had been bidding on modeled data for users who had opted in. The fix was a small PHP snippet at the right priority.
01 The numbers
- gcs=G1-- (denied) Consent state on beacons, before
- gcs=G111 (granted) Consent state on beacons, after
- Same day Diagnosis to verified fix
- One toggle Rollback plan
02 The breakdown
The Setup
A made-to-order home brand on WooCommerce, with Google Ads as its biggest paid channel. The site ran a consent banner, visitors were accepting it, and on the surface everything about the tracking looked compliant and normal.
I was on the account for a different reason: consolidating a redundant tracking stack that had accumulated six overlapping tracker installs across the stack, with four separate Google tag libraries loading on a single page. The consent finding came out of that audit, and it outranked everything else on the list.
Six installs is what happens over years rather than in one bad decision. A plugin adds a pixel, an agency adds a container, a developer hard-codes a tag into the theme, and nobody removes anything because nobody is certain which one the reporting depends on. I audit these stacks by reading what the page sends rather than what is installed, which is how the consent problem surfaced. The installed list looked fine. The outbound requests did not.
What Was Broken
I measured a normal returning-visitor page load with consent verified as granted. The consent cookie said yes on every category. The consent platform’s own API said yes. And every consent-scoped Google beacon on the load still went out carrying gcs=G1--, which is Consent Mode’s code for ad storage and analytics storage both denied.
Google Ads, the biggest spend on the account, fired its conversion and remarketing beacons in denied mode, which means modeled conversions and degraded remarketing instead of observed data. Merchant Center fired denied on the same load.
Denied is not the same as missing, which is what makes this so hard to notice. In denied mode Google still receives a cookieless ping and still reports a conversion count, estimated from patterns across similar traffic. The dashboard looks populated. What is gone is the observed record of which click produced which sale, and everything that depends on it: remarketing list membership, Customer Match matching, and the granularity Smart Bidding uses to tell one audience from another. A brand paying for consent management to obtain first-party data was handing Google an estimate of its own customers.
The root cause had two layers:
- The consent platform’s free tier never pushes the update. It sets Consent Mode’s default state to denied, as it should. But for a returning visitor who already accepted, it never sent the follow-up granted signal. That feature is behind the paid tier. Nothing on the page ever told Google about the yes.
- An earlier bridge script fired too late. A tag that pushed the granted signal existed, but it ran on DOM-ready and read the consent platform’s JavaScript API, which loads late. By the time it fired, the page-view and conversion beacons were already gone, denied.
The brand was paying for consent management and losing the data of exactly the users who had consented. Nobody had looked at the beacons.
What I Did
The fix reads the consent cookie directly, server-side, and prints the granted signal into the top of the page before any tag can fire. A small PHP snippet hooked at head priority 1. It does not wait on the consent platform’s JavaScript, so the race condition is gone by construction.
Priority 1 is the entire design. Consent Mode is order-dependent in a way most implementations treat as a detail: the state a tag reads is whatever the state was at the microsecond the tag fired. A granted signal that arrives correctly but late is the same as no signal, because the beacons it was meant to authorize have already left. Every fix that reads the consent platform’s JavaScript API inherits that platform’s load order and therefore inherits the race. Reading the cookie server-side removes the dependency instead of sequencing around it, which is why the fix holds through plugin updates and tag manager changes.
Keeping the vendor’s paid tier out of it was deliberate. That upgrade solves the symptom and leaves the data path dependent on a vendor script running before Google’s. The snippet costs nothing per month and fails in the safe direction: if it stops running, the state falls back to denied.
Verified across repeat loads the same day: the Google Ads and Merchant Center beacons flipped from gcs=G1-- to gcs=G111 and stayed there. Storefront healthy, consent choices still respected. Rollback is deactivating one snippet.
What I Did Not Claim
This fix restores observed data going forward for consented visitors. It does not backfill the modeled months, and it does not change data for visitors who decline, which is exactly as it should be. I also chased a suspicion to a dead end on purpose: the suspected GA4 double-counting on this stack turned out to be false once tested properly, and saying so saved the client a pointless migration.
The Takeaway
Consent Mode has a failure state nobody watches for: default-denied with no update. Your banner can be legally perfect and your data pipeline still starving, because the two are separate systems and only one of them has a dashboard.
The test takes five minutes and needs no tooling beyond a browser. Accept your own banner. Reload the page. Open the network tab, filter for collect or google, and read the gcs parameter on the outbound request. G111 means both storage types granted and the event observed. G100 means analytics granted and ad storage denied, which breaks remarketing while analytics looks fine. G1-- after you said yes means nothing on the page ever told Google about the yes, and your ad platform is guessing at conversions you paid to observe.
Run it as a returning visitor, not in a fresh incognito window. The first-visit path is the one every implementation gets tested on and the one that usually works. This failure lives on the second visit, where the banner does not reappear and the granted signal has to come from a stored choice that something must remember to send.
Where this sits in the wider architecture is layer two of the Tracking Stack, the container. Consent state is a property of the container’s load order, which is why it belongs above every platform-specific fix and gets checked before anyone argues about attribution windows.
Want the beacons on your site read properly? Start with a tracking audit.
03 More work
Other accounts, same approach.
2 add-to-carts recorded in ten days, before
The server-side tracking spine was wired, purchases flowed cleanly, and the container looked healthy. It was also blind to the entire mid-funnel: two add-to-carts recorded in ten days on a store doing real volume. The root cause was one missing subscription. The fix shipped and verified the same day, and the restored funnel now carries 500+ ad-attributed add-to-carts a month, from under two.
Read the breakdown→1, from 2 competing ga4 installs on one property
A high-ticket furniture store had a sophisticated server-side tracking stack on paper and a GA4 property that filed its revenue under Unassigned. The diagnosis found three separate failures stacked on top of each other. The fix was one checkbox, two surgical cuts, and one lowercase letter.
Read the breakdown→Ready to talk
Want this kind of breakdown on your account?
Thirty minutes on the phone. One clear set of next steps. Same person on the call as on the work.