AuditTags
Consent ModeGoogle AdsGA4MetaAttributionPrivacy

Consent Mode v2 in 2026: Why GA4 and Google Ads Disagree

GA4 says one number, Google Ads says another, Meta a third. Consent Mode v2 is the silent cause — here's how to find and fix it.

A
AuditTags Engineering
Paid-Ads Tracking Specialists
11 min read
Consent Mode v2 in 2026: Why GA4 and Google Ads Disagree

Your GA4 says one number. Google Ads says another. Meta a third. Somebody just tell you which one is real before you spend more.

If you're an owner-operator running paid ads, you've probably hit this. You add up the numbers across dashboards, they don't match, and nobody — not your agency, not the support docs, not the Google Tag Assistant green check — can tell you which is the truth. The most common diagnosis we see isn't ad creative, isn't bidding, isn't audience. It's that Consent Mode v2 is silently dropping a chunk of your data on the floor, and the dashboards keep going green because nothing technically errors out.

We learned that the hard way running our own Google Ads campaigns. Across 17 paid clicks over two windows, GA4's Data API returned zero sessions attributable to Google CPC. The Ads dashboard saw the clicks. GA4 saw nothing. Two missing lines of Consent Mode v2 configuration, plus a misconfigured account-level URL suffix, were the entire cause. We spent $124 to learn that. This post is what we wish we'd known on day one.

TL;DR

  • Consent Mode v2 has four consent types — analytics_storage, ad_storage, ad_user_data, ad_personalization. All four must be set to denied by default before any tag fires, then updated when the visitor accepts.
  • Two settings most setups miss: gtag('set', 'url_passthrough', true) and gtag('set', 'ads_data_redaction', true). Without them, GA4 sends nothing for visitors who haven't accepted cookies — i.e. every first-time visitor.
  • The gcs URL parameter on every GA4 hit tells you what consent state actually reached Google. G100 = consent denied. G111 = consent granted. If gcs stays G100 after the visitor clicks Accept, the update never reached the tags.
  • A green Tag Assistant doesn't mean tracking is working. Tag Assistant validates the tag, not the consent signal.

Why this is biting now

Consent Mode v1 had two consent types. Version 2, mandatory for EU traffic since March 2024, added two more — ad_user_data and ad_personalization. If you only set the original two, your Google Ads features that depend on the new ones (Enhanced Conversions, remarketing, conversion modelling) fail silently. No banner in the UI. No console warning. Just smaller audiences, lower conversion counts, and no obvious cause.

Layer that on top of the basic problem — most "Consent Mode setups" are actually just consent banners that store the user's choice in their own database without ever telling Google what was decided — and the result is what L6 forum-mining surfaces every week: SMB owners with thousands in monthly spend, looking at three dashboards that disagree, paying individuals $500–$3000 to fix it, and most of those fixes don't take.

If your symptoms include any of the following, Consent Mode v2 is in the suspect list:

  • GA4 shows 30–60% less traffic than your server logs or backend records
  • Google Ads dashboard shows 8 conversions; GA4 shows 700 (or vice versa)
  • Conversions used to work and stopped some time after March 2024
  • Your CMP shows a 60%+ accept rate but GA4 doesn't reflect that traffic at all
  • Enhanced Conversions and remarketing audiences show "list too small"

What "correct" actually looks like

A working Consent Mode v2 setup has three parts that all have to land:

  1. Default state, set before any tag loads. All four consent types denied (for EU/EEA visitors), with url_passthrough and ads_data_redaction set to true.
  2. Update on user action. When the visitor clicks Accept, your CMP must call gtag('consent', 'update', ...) with the granted values. Storing the preference in the CMP's database is not the same thing.
  3. Tags configured to respect consent. Each GA4 / Google Ads / Floodlight tag has a "Require consent for tag to fire" setting that must be configured.

Miss any one and you get one of the seven failure patterns below.

Pattern 1: No default state set

What's going wrong

Your tag manager loads before any consent state is established. GA4 fires immediately with full tracking enabled. Cookies drop on page load. The consent banner appears a fraction of a second later, but the damage is done — you've already tracked an unconsented visitor.

How to check it yourself

Open DevTools → Network tab and filter for collect. You'll see GA4 requests firing before the banner appears. Check Application → Cookies — _ga and _gid exist before any banner interaction. In GA4 DebugView, the first pageview shows no gcs parameter.

Fix steps

  • Add the consent default snippet directly to <head>, BEFORE the GTM/gtag snippet
  • Set all four consent types to denied in the default
  • Use the region parameter to target only EU/EEA countries
  • Verify in Network tab that the consent command fires before any tag loads

Pattern 2: Missing url_passthrough and ads_data_redaction (the silent killer)

What's going wrong

Your default is correctly set to denied. Your CMP fires the update on accept. Tags respect consent. And yet GA4 shows zero sessions from Google Ads, because without url_passthrough and ads_data_redaction, GA4 sends nothing on consent-denied pageviews. Cookieless pings — the mechanism that lets Google model conversions for visitors who haven't accepted — never go out. Your first-time visitors, which is most paid traffic, become invisible.

This is the one we spent $124 to find. It's also the most common misconfiguration we see in the wild because every Consent Mode v2 implementation guide stops at "set the four flags to denied" and skips these two extra lines.

How to check it yourself

Open your marketing pages in DevTools with consent denied (clear cookies, decline the banner). The Network panel should show requests to https://www.google-analytics.com/g/collect containing gcs=G100 and a dma= parameter. No request at all = these settings are missing.

Fix steps

Add these two lines to your Consent Mode default snippet, right after the gtag('consent', 'default', ...) call:

gtag('set', 'url_passthrough', true);
gtag('set', 'ads_data_redaction', true);

url_passthrough preserves the gclid ad-click ID through redirects so Google Ads can still attribute the conversion model. ads_data_redaction strips identifying data from cookieless pings so they're privacy-compliant. Together they make the whole consent-denied path work.

Pattern 3: CMP doesn't communicate with Google tags

What's going wrong

Your consent banner records preferences correctly. Visitors click Accept, the CMP logs it. But the CMP never fires the gtag('consent', 'update', ...) command. Tags still think consent is denied.

How to check it yourself

After clicking Accept, navigate to a new page. In the Network tab, find the GA4 collect request and look at the gcs parameter. G111 = all granted. G100 = all denied. If gcs stays G100 after acceptance, the update isn't reaching the tags.

Cross-check the CMP dashboard: if it reports a 65% accept rate but GA4 traffic is only 40% of your backend's session count, the gap is consent updates not firing.

Fix steps

  • Verify your CMP is on Google's certified list for Consent Mode v2 (Google maintains a public list)
  • Cookiebot, Consentmo, OneTrust, Iubenda, Termly all have native Consent Mode v2 integration when configured correctly
  • Older or cheaper banner solutions often don't fire the update at all
  • Check browser console for JavaScript errors from the CMP — silent JS failures kill the update path

What's going wrong

Default is set. CMP fires the update. But the GA4 / Google Ads tag in GTM isn't configured to respect consent signals. It fires regardless of consent state.

How to check it yourself

Decline consent on a fresh visit and check Network for collect requests. If they fire with gcs=G100 (denied) but the tag still sends event data and sets cookies, the tag isn't honoring consent.

Fix steps

  • Open your GA4 Configuration tag in GTM
  • Under "Consent Settings," configure "Require additional consent for tag to fire"
  • Set analytics_storage to "Granted" requirement
  • Repeat for every Google Ads conversion tag and Floodlight tag
  • For Meta Pixel: there's a separate fbq('consent', 'revoke') / fbq('consent', 'grant') flow — the Google Consent Mode flags do not control Meta

What's going wrong

Your setup handles analytics_storage and ad_storage from v1 but doesn't set ad_user_data or ad_personalization that were added in v2. Google Ads features that depend on the new types fail silently.

How to check it yourself

In the Network tab, look at a collect request. The gcs parameter is two characters of consent state encoded together. The full state with all four granted is G111 plus you should see gcd= containing the full four-flag matrix. If only the first two flags are present, you're on a v1 setup.

Fix steps

  • Update your consent default to include all four types
  • Verify your CMP maps user choices to all four parameters
  • ad_user_data controls whether user data goes to Google servers
  • ad_personalization controls remarketing and personalized ads functionality

Pattern 6: Over-restricting non-EU regions

What's going wrong

Your default consent denies everyone globally, including US visitors who never needed prompting. Americans get behavioral-modeling-only data, which is less accurate and less complete.

How to check it yourself

Compare US session counts in GA4 against your backend. If GA4 is materially lower for US than for backend reality, and your region parameter is empty or ["*"], you're over-restricting.

Fix steps

  • Add the region parameter to consent default configuration
  • List only EU/EEA country codes (and UK, plus any others where you have a specific obligation)
  • Non-listed regions default to granted automatically
  • Don't restrict US/CA/AU unless you have a specific privacy requirement (Quebec Law 25, CCPA/CPRA, etc.)

What's going wrong

Your main site has Consent Mode properly configured. But your high-volume landing pages — promotional URLs, page-builder-generated routes, AMP pages, embedded iframes — don't inherit the consent setup. Those pages track everyone with full permissions.

How to check it yourself

Run the Network-tab check on every distinct landing page template, not just the homepage. Specifically check anything generated by a page builder, anything served from a different subdomain, and any AMP or AMP-fallback page.

Fix steps

  • Move the consent default to Google Tag Manager's container init (fires on every page that has GTM, regardless of which template rendered it)
  • Verify every landing page actually loads the GTM container (some page builders strip it)
  • Test promotional URLs in incognito with cleared cookies before launching paid campaigns at them

The diagnostic that cuts through all of this

If you're not sure which of the seven patterns applies to you, run this one query against the GA4 Data API. It tells you, in 30 seconds, whether your paid Google traffic is reaching GA4 at all:

// ga4-paid-funnel.mjs — run with: node ga4-paid-funnel.mjs
import { BetaAnalyticsDataClient } from '@google-analytics/data'

const client = new BetaAnalyticsDataClient({
  credentials: JSON.parse(process.env.GA4_SERVICE_ACCOUNT_JSON),
})

const [resp] = await client.runReport({
  property: `properties/${process.env.GA4_PROPERTY_ID}`,
  dateRanges: [{ startDate: '14daysAgo', endDate: 'today' }],
  dimensions: [{ name: 'date' }],
  metrics: [
    { name: 'sessions' },
    { name: 'totalUsers' },
    { name: 'eventCount' },
  ],
  dimensionFilter: {
    andGroup: {
      expressions: [
        { filter: { fieldName: 'sessionSource', stringFilter: { value: 'google' } } },
        { filter: { fieldName: 'sessionMedium', stringFilter: { value: 'cpc' } } },
      ],
    },
  },
})

console.log(resp.rows ?? '(no rows)')

If the result is (no rows) after running ads for 24+ hours and your Ads dashboard shows clicks, attribution is broken. Remove the paidFilter and re-run — if even unfiltered traffic is empty, the gap is at the consent layer (Pattern 2 above). If unfiltered shows traffic but paid doesn't, the gap is at the Ads ↔ GA4 link or your URL suffix (see "Two more things that are probably also wrong" below).

Two more things that are probably also wrong

If you've fixed Consent Mode v2 and the dashboards still don't agree, check these next. We hit both:

The Google Ads ↔ GA4 property link. Even with consent working, GA4 won't classify Google traffic as "Paid Search" unless the two properties are linked at the account level. Without the link, paid sessions show up as sessionSource=(not set), sessionDefaultChannelGroup="Unassigned". Fix in Google Ads → Tools → Linked accounts → Google Analytics (GA4) → Link. Wait 24h, then re-run the query above.

Final URL suffix on every ad. If you want server-side / DB attribution (your own analytics, not just GA4's), every ad's final URL needs UTM parameters. Set this once at the account level — Google Ads → Admin → Account settings → Tracking → Final URL suffix:

utm_source=google&utm_medium=cpc&utm_campaign={campaignid}&utm_content={adgroupid}&utm_term={keyword}

A gotcha we paid for: longer suffixes that include utm_creative={creative}&utm_matchtype={matchtype}&utm_device={device} get rejected by the validator in some locales without telling you why. The shorter form above saves cleanly. Also: leave the "Tracking template" field empty. A non-URL value there silently breaks click tracking on every ad in the account.

What to do this week

If you're running paid Google or Meta ads and your dashboards don't agree, here's the order:

  1. Run the GA4 Data API query above. If it returns (no rows) for Google CPC, you have a Consent Mode v2 problem, an Ads-link problem, or both.
  2. Open your site in incognito. Decline consent. Check Network for collect requests. If you see them with gcs=G100, Consent Mode v2 is working at the consent-denied path. If you see nothing, you're missing url_passthrough and ads_data_redaction (Pattern 2).
  3. Accept consent. Navigate to another page. Check the next collect request's gcs. If it stays G100, your CMP isn't firing the update (Pattern 3).
  4. If consent flow is clean but Ads still doesn't attribute, link the Google Ads and GA4 properties and set the Final URL suffix (above).
  5. After any change, wait 24 hours before re-querying. GA4 attribution and Ads click reporting are not real-time.

A note on what this post does and doesn't do

This post is a manual diagnostic playbook. It tells you what to check, in what order, and what the symptoms mean. It's based on actual incidents we've debugged on our own paid campaigns, plus patterns we keep seeing surface in the SMB advertiser threads on r/PPC, r/googleads, and r/FacebookAds.