Consent · PMs · engineers · marketers
A banner that does not block still fires the pixel
Legal asked for a banner. Engineering added a div. The Meta pixel in head does not read the div. Network tab is the audit, not a screenshot of the modal.
What blocking looks like
No collector request until grant, or a request with Consent Mode denied flags and without advertising cookies. If facebook.com/tr fires on landing with a full _fbp write before click, you did not block. A modal, a footer bar, or a wrapper that only CSS-hides the page still lets GTM run All Pages tags on Container Loaded.
An ad blocker that matches facebook.com/tr never starts the request. A CMP that never queued fbq looks the same in a sparse Network tab. Disable the extension, keep the banner up, watch whether the request appears. If it appears, you are not blocking. If it never appears with the extension off, the wrapper is holding, or the tag is broken. Those two failures need different owners.
https://www.facebook.com/tr?id=1234567890123456&ev=PageView&noscript=1
GTM load order
Built-in Consent Overview is the map of which tags require which signals. A custom HTML tag that injects fbq ignores that map unless you added a consent check. Custom HTML is where banners go to die. Blocking is a load order: CMP stub, consent default denied, wait, update, then fire.
Consent Initialization before Container Loaded is not optional decoration. Tags that fire on Container Loaded with no consent requirement will run under the banner. Moving the pixel to a custom HTML tag named wait for CMP does nothing unless the tag actually waits. Read the tag's consent requirement, then refuse on a fresh profile, then watch Network.
First paint is the leak
QA on a second pageview is not QA. The first view is the one that leaks. Use a fresh profile, refuse, accept, and refuse again. Confirm the collect and ads requests change. Confirm a fired URL in Pixellint carries a decoded string, not a placeholder, once the user has accepted and the IAB hop is allowed to fire.
SPA routers make this worse. A client-side navigation can fire All Pages again without re-showing the banner. If the first view leaked, every virtual pageview leaked. If the first view held, confirm the router does not inject a second container that has no consent wrapper.
CAPI outruns the banner
A server event can outrun the banner if your backend sends Purchase on order create regardless of consent. Same policy question, different pipe. Decide per event type. Server-side GTM and CAPI will still fire if you did not gate those too.
A blocked browser tag plus an ungated Purchase postback is how you honor the banner in Chrome and ignore it in the warehouse. Store the CMP choice with the session or the order. Skip CAPI, or send a redacted event, when the basis is missing. Consent Mode updates gtag. It does not update your shop backend.
After grant, still a real string
Once the user accepts, the next hop is parameters on the pixel: gdpr=1, a real TC String, GPP, Consent Mode updates. A banner that blocks and then fires gdpr_consent=true still failed. Blocking was the easy half. The wire still has to carry a decoded v2 string, not a boolean.
pixellint validate url on the fired request after accept. Look for core.privacy.tc_string_version if the first six bits are not 2. Look for core.privacy.gdpr_consent_malformed if the alphabet is wrong. Look for nothing if the CMP mapped correctly. The modal screenshot does not substitute for that paste.
https://example.com/px?id=1&gdpr=1&gdpr_consent=true
Check the artifact
Paste the pixel URL or JSON body into the
playground. Same engine as
pixellint validate. Nothing leaves the tab.