A shopper lands from Google Ads with gclid on the query string. Your CMP is still on default deny. Marketing enabled URL passthrough so the id survives the first internal click. The second page URL looks healthy in the address bar. Floodlight still behaves like tracking is off.
That is not a broken redirect. It is Consent Mode doing two different jobs at once.
Passthrough moves parameters. It does not flip ad_storage.
Google Consent Mode v2 split ad_storage from analytics_storage so tags can wait for a CMP signal before they set advertising cookies. URL passthrough is a separate gtag('set', 'url_passthrough', true) call. Google's consent guide describes it as a way to append click-related query parameters on same-registrable-domain outgoing links when consent mode is implemented and the tag is consent-aware. Typical parameters include gclid, dclid, gclsrc, _gl, and wbraid.
Passthrough helps the next page load carry the click id in the URL so a consent-aware tag can use it if storage becomes available later in the session. It does not change ad_storage from denied to granted. If your conversion tag fires on Container Loaded before the CMP calls gtag('consent', 'update', { ad_storage: 'granted' }), the tag still runs in denied mode even though the URL shows gclid.
ads_data_redaction is the other half teams enable in the same breath. When ad_storage is denied and redaction is true, Google documents that ad click identifiers sent in network requests by Google Ads and Floodlight tags will be redacted. Redaction has no effect when ad_storage is granted, or when you are not using gtag('consent') at all.
The hardened default many EEA sites ship looks like this:
gtag('consent', 'default', {
ad_storage: 'denied',
analytics_storage: 'denied',
wait_for_update: 500
});
gtag('set', 'url_passthrough', true);
gtag('set', 'ads_data_redaction', true);
Syntax is fine. Trigger order is where production breaks: a Conversion Linker or Google Ads conversion tag bound to All Pages races the banner. Passthrough preserves gclid on page two. Redaction strips the click id from the ad network request while storage is denied. Neither line grants ad_storage.
Why this is not TCF, GPP, or a CMP bug
IAB TCF and GPP strings describe vendor legal bases on the wire to adapters. Consent Mode is Google's tag-side interpretation for Google tags. You can decode a valid TC string on a Floodlight URL and still run gtag in denied ad_storage until the Google-specific update runs, if your CMP maps TC segments to Consent Mode incorrectly.
us_privacy and gpp / gpp_sid on the pixel are a different grammar again. A bid request can carry regs.gpp with sid 7 while the browser tag never forwarded the same composite string. Duplicate consent parameters are yet another class of failure: two gpp keys, or the same TC String copied into both gdpr_consent and gpp with conflicting bits.
The passthrough mistake is narrower: teams treat a visible gclid as proof conversions work, while Google's ad tags follow consent state, not the query bar alone.
Server-side Google hits have the same split. Consent Mode parameters must travel with the Measurement Protocol or Google Ads upload. A CAPI-style server payload that omits consent fields while the web tag had ad_storage denied is not repaired by passthrough on the browser. Passthrough never ran on the server hop.
How you catch it
Start with two captures on the same session: one network request from the conversion tag before the CMP update, one after grant. If redaction is on and ad_storage is denied, Google documents redacted click ids on ad tag requests even when the page URL still shows gclid.
Log gtag consent state at conversion fire time in Tag Assistant or your own consent log. URL parameters alone are not proof of granted ad_storage.
For third-party pixels on the same page, validate what each vendor actually received on that hop. I maintain Pixellint, an open source linter for pixels and conversion API payloads. The core privacy pack decodes gdpr_consent and gpp header bits on live URLs. It will not flip your CMP banner. It will tell you whether the Floodlight or affiliate URL you captured still carries a TC String that decodes to grant after the user rejected, or a malformed gdpr_consent=1 that is not a TC String at all:
$ pixellint validate url 'https://ad.doubleclick.net/ddm/activity/src=1234567;type=convr0;cat=purch0;gdpr=1;gdpr_consent=1'
error core.privacy.gdpr_consent_malformed
`gdpr_consent` is `1`, whose first six bits decode to version 1.
...
That error is a wire contract check, not a substitute for Consent Mode debugging. Pair URL linting with trigger-order review: Consent Initialization first, CMP update before ad conversion tags, passthrough set consistently on every Google tag in the container. Google notes mixed containers where only one tag sets passthrough produce the same split-brain attribution as mixed pixel and CAPI clocks.
Paste a URL into the browser playground if you do not want a local install. For field tables that map TC, GPP, and tag parameters side by side, see consent on the wire.
Where to go next
The dated argument on passthrough versus redaction lives in the Consent Mode URL passthrough post. The Google Consent Mode v2 contract page lists ad_storage, analytics_storage, ad_user_data, and ad_personalization and repeats that Consent Mode is not a CMP.
If your stack also forwards IAB signals into OpenRTB, read the GPP guide for header type 3 and section ids. EU inventory still belongs on gdpr plus gdpr_consent, not a TC String pasted into gpp. OpenRTB regs.gpp and regs.gpp_sid must agree with what the pixel sent; mismatched section ids are a separate hop problem from passthrough, but the same QA habit applies: compare auction capture to fired tag URL on one page view.
For regulatory transparency fields arriving on bid requests, DSA transparency in OpenRTB is the server-side counterpart to consent strings on tags. Fix Google tag order first: default deny, passthrough and redaction as set calls, CMP update before conversion tags, then verify granted state on the tag that actually sends ad network requests.
The short version
url_passthrough keeps click ids on your URLs across same-domain navigations. It does not grant ad_storage. With ads_data_redaction true and storage denied, Google ad tags redact click identifiers on network requests anyway.
A visible gclid after an internal click is continuity on your site. Google conversion attribution follows consent state at fire time. Audit trigger order, log consent at conversion, and lint the non-Google pixels on the same page so you are not debugging the wrong contract.
Pixellint is independent of Google and IAB. Rule ids cite their docs because that is where the requirements live, not because this is an official tool.
Top comments (0)