GTM Consent Mode V2 on WordPress: Why Most Implementations Are Silently Broken

April 10, 2026
by Cherry Rose

Your consent banner is live. GTM Preview shows green. You clicked Reject All and nothing seemed to fire. So your Google Consent Mode V2 implementation is working — right?

Probably not. Most WordPress stores have a broken Consent Mode V2 setup that passes data for users who explicitly said no — and GTM Preview mode will never tell you. The failure modes are invisible in standard testing, which is exactly why they persist for months without anyone noticing.

There are three specific failure patterns responsible for the majority of broken implementations on WordPress. This article names them, explains why each one is invisible in GTM testing, and shows you how to verify the Reject All path actually holds.

The Signal Most Stores Miss Entirely

Here is the diagnostic that most guides skip. After implementing Consent Mode V2, a 5-10% dip in your conversion data is normal — that is the expected impact of users in your market exercising their rights. If you see a 30%+ drop, that indicates a misconfiguration on the technical side (Cookietrust, 2026).

But if you have significant EU traffic and you see no conversion dip at all, that is an equally serious red flag. It almost certainly means your consent signal is not suppressing anything. Tags are firing for everyone, regardless of what they chose on your banner.

This is the counterintuitive part of Consent Mode V2 debugging: the absence of a data impact is often evidence that your implementation is broken, not evidence that it is working cleanly.

You may be interested in: Does Your WooCommerce Tracking Plugin Fire Pixels Before Consent Is Given?

This is the most common and most misunderstood failure mode on WordPress. Caching plugins like WP Rocket and W3 Total Cache are built to serve identical pages to every visitor as fast as possible. That is their job. The problem: Consent Mode V2 depends on a fresh, user-specific consent state being read on every page load.

Here is what happens. Visitor A comes to your site, accepts all cookies, and leaves. WP Rocket caches that page — including the scripts that initialized the dataLayer with a granted state. Visitor B arrives later, sees the consent banner, clicks Reject All. But the page they are served from cache still contains Visitor A’s granted initialization. Their rejection is logged in the consent banner, but it is writing over a dataLayer that has already been set to granted. Tags have already fired.

Caching plugins must be configured to exclude the consent banner script from caching and to serve uncached pages to users with no prior consent cookie. Without this configuration, your consent implementation is cosmetic — the banner appears, the button works, but the dataLayer is being pre-populated with the wrong state (Cookietrust, 2026).

To check: clear your cache completely, open an incognito window, and watch your browser Network tab as the page loads. If you see requests to google-analytics.com or googleads.g.doubleclick.net before you have interacted with the consent banner at all, your caching configuration is the problem.

Consent Mode V2 has a strict requirement that is almost never explained in setup guides: the consent initialization must happen before the GTM container loads. Not alongside it. Before it.

If your WordPress theme or a plugin loads scripts in the document head before the GTM snippet, and your consent banner initializes after page load, your consent signal is arriving too late (Cookietrust, 2026). GTM fires with no consent state defined, falls back to default behavior, and your tags run — all before the user has seen the banner.

The correct sequence: consent initialization fires in the head, sets the default denied state across all four consent types, then GTM loads, reads that denied state, and holds its tags. The banner then appears. If the user grants consent, the consent update fires and GTM releases the appropriate tags.

Consent Mode V2 introduced two new consent types — ad_user_data and ad_personalization — on top of the original ad_storage and analytics_storage. All four must be present in your initialization for full compliance (CookieHub, 2026). Missing ad_user_data or ad_personalization means your implementation is running V1 behavior under a V2 label.

To check: open your page source and search for the GTM snippet. Then search for gtag consent default. The consent default call must appear above the GTM script tag. If GTM appears first, you have a loading order problem.

You may be interested in: Your Server-Side GTM Is Probably Leaking Consent Data (Here’s Why)

WordPress’s plugin ecosystem makes it easy to accidentally install two solutions that both try to manage consent. This happens more than you would think: one plugin came pre-configured with a theme, another was installed separately when Consent Mode V2 became required, and both are now active.

Running multiple consent plugins simultaneously causes consent state conflicts — only one solution should ever manage the dataLayer at a time (WPConsent, 2026). When two plugins both try to write consent states, they can overwrite each other in unpredictable order. The result is a dataLayer that flickers between granted and denied states, with the final state depending on script load timing that varies per page load.

This failure mode is particularly difficult to detect because it can appear correct in some testing sessions and broken in others. If your Consent Mode V2 behavior seems inconsistent — sometimes tags fire after rejection, sometimes they do not — conflicting plugins are the most likely cause.

To check: review every active plugin. Anything with cookie, consent, GDPR, or privacy in the name should be reviewed. You need exactly one solution managing consent. Deactivate all others and test again from a clean browser session.

How to Verify the Reject All Path Is Actually Holding

Standard GTM Preview mode testing is not sufficient for Consent Mode V2 verification. Here is the test that actually confirms your implementation is working.

Open a fresh incognito window with browser developer tools open. Load your site. When the consent banner appears, click Reject All. Then watch the Network tab — filter for requests containing google-analytics, googleads, doubleclick, and facebook. None of these should fire after your rejection click. If any request goes out to these endpoints, your implementation is not suppressing what it should be.

Repeat this test three times in separate incognito sessions. A working implementation blocks these requests reliably on every test, not just most of the time. Inconsistency across sessions points directly to caching or plugin conflicts.

Getting your web GTM consent configuration correct is the necessary first step. But it is worth understanding the structural limit of what you have fixed. Web GTM consent works at the browser level — it controls which client-side tags fire based on the consent signal your banner sends to the dataLayer.

The Transmute Engine™ operates at the server layer, where consent propagation works differently. Server-side event pipelines must receive the consent signal from the web container and respect it when routing events to destinations. A correctly configured web Consent Mode V2 setup sends the right signal — but a server container that is not consent-aware will ignore it and send data regardless. Once your web container is verified, that is the next failure point worth checking.

Key Takeaways

  • No conversion drop with EU traffic means your consent is probably not suppressing anything — absence of impact is a red flag, not proof that it is working.
  • Caching plugins must exclude consent scripts from caching — without this, cached pages deliver the wrong consent state to new visitors.
  • Consent initialization must appear before the GTM script tag in page source — loading order is not optional.
  • All four consent types must be initialized — ad_storage, analytics_storage, ad_user_data, and ad_personalization.
  • Only one consent plugin should manage your dataLayer — two plugins in conflict produce unpredictable results.
  • Test with the Network tab, not just GTM Preview — filter for actual outbound requests to advertising endpoints after a Reject All action.
Why are my GA4 tags still firing after a user clicks Reject All?

The most common causes are wrong script initialization order (your consent banner loads after GTM fires), caching plugin interference serving a stale granted state, or two consent plugins writing conflicting values to the dataLayer. Check GTM Preview mode with your Network tab open — filter for google-analytics.com requests to see if GA4 is receiving data despite the denial.

Does GTM Consent Mode V2 work with WP Rocket or W3 Total Cache?

It can, but only with specific exclusions configured. Caching plugins must exclude the consent banner script from caching and serve uncached pages to users in a new session. Without this, the cached page can deliver a granted state from a previous visitor, tracking your new visitor without their consent.

How do I test that Consent Mode V2 is actually blocking tags on WordPress?

Open a fresh incognito window with developer tools open. Load your site, click Reject All on the consent banner, then watch the Network tab filtered for google-analytics.com, googleads.g.doubleclick.net, and facebook.com/tr. None of these should receive requests after your rejection. Repeat across three sessions to rule out caching inconsistencies.

What are the four consent types required for Consent Mode V2 compliance?

Google Consent Mode V2 requires ad_storage, analytics_storage, ad_user_data, and ad_personalization. All four must be initialized with a denied default before GTM loads. Missing ad_user_data or ad_personalization means your implementation is running V1 behavior under a V2 label.

Once your web container consent chain is verified, the next question is whether that signal is being respected downstream. If you are running server-side tracking, the consent signal has to propagate correctly through that layer too — or the fix you made at the web level becomes irrelevant at the point where your data actually moves.

Share this post
Related posts