How to set up Google Consent Mode v2 on Shopify

Put your Consent Mode defaults, set to denied, in theme.liquid above the container, then have your banner write the visitor's choice to two places on the same click: Google Consent Mode, and Shopify's own Customer Privacy API. Shopify runs two consent systems, not one. Wire only the Google half and your Custom Pixels keep firing whatever the visitor chose.
Shopify has two consent systems, and only one of them is Google's
Most guides to this topic tell you how to switch on a consent app and paste a container. The part that decides whether the setup holds is structural and specific to Shopify: consent is enforced in two separate places that know nothing about each other unless you connect them.
Shopify's Customer Privacy API governs what the platform controls: Custom Pixels and app pixels. Nothing you do in Google Tag Manager reaches it. It listens for one thing, Shopify.customerPrivacy.setTrackingConsent(), and until something calls that, it holds whatever state it already had.
Google Consent Mode v2 governs your own tags: the container in theme.liquid and everything it fires. It listens for gtag('consent', ...) and has no idea the Customer Privacy API exists.
So there are two failure shapes. Wire only Consent Mode and your GA4 and Ads tags behave while Custom Pixels and app pixels carry on unblocked. Wire only the Shopify side and your own container fires ungated. A consent app from the App Store usually handles both, which is why that route is easier; a CMP pasted into the theme does not, unless you write the callback yourself.
Getting this right is not a rounding error. Across Amplio Data client implementations we have measured 34% of sessions hidden by consent, and 20% to 40% of that signal recoverable once the wiring is correct. Those are measured ranges across Amplio Data client implementations, not a guarantee. Recovery depends on your traffic mix, your regions and how your tags are configured.
Setting it up
Decide which banner owns the decision
Shopify's own banner, under Settings then Customer privacy, or a CMP. Shopify's banner writes to the Customer Privacy API for you but does not speak Consent Mode, so you still bridge it to your container. A CMP installed as a Shopify app usually does both sides; one pasted into the theme as a script does the Google side only until you add the callback. Step six is what tells you which halves yours actually covers.
Set denied defaults above the container
A Consent Mode default command setting
ad_storage,analytics_storage,ad_user_dataandad_personalizationtodenied, placed intheme.liquidbefore the container snippet, not after it. This is the ordering the whole setup rests on: a default that arrives after the container has already fired a tag is not a default, it is a correction that came too late. If you serve regions outside the EEA, scope the defaults by region rather than denying everywhere.Keep the banner in the theme, never in a Custom Pixel
Custom Pixels run inside a sandboxed iframe with its own opaque origin. A banner cannot render into the page from there, and cookie writes from inside it do not behave like first party writes. The banner belongs in
theme.liquid, in the main page context, above the container.Write the choice to both systems on the same click
On accept or reject, send the Consent Mode update, and in the same handler call
Shopify.customerPrivacy.setTrackingConsent()with the matching categories. If you skip this half, Custom Pixels and app pixels never learn the visitor said no. This is the single most common gap we find on Shopify stores that believe they are done.React to the choice in the same page view
Shopify fires a
visitorConsentCollectedevent when consent is recorded. Listen for it and apply the state rather than waiting for the next navigation, so a visitor who accepts on the collection page is measured on that page rather than from the next one onward.Verify on the live storefront, on both paths, and check out separately
Not in preview. On the deny path your GA4 request should still leave with a
gcsvalue showing denial rather than not leave at all, Custom Pixels should be absent, and no advertising cookies should be set. On the accept path all of it should appear. Then repeat the whole check inside checkout, which runs in its own sandbox and can behave differently from the storefront you just tested.
The ordering argument without the Shopify specifics is in why tags fire before the cookie banner loads, and basic versus advanced Consent Mode tells you which one you ended up running faster than reading your own container will.
Three things that surprise people on Shopify
Pending is not accepted. Before a visitor chooses, the Customer Privacy API reports a pending state. A bridge that treats anything other than an explicit no as a yes leaks on every first page view. Treat pending as denied.
Returning visitors from before the banner went live may never see it. On one store where we expanded a Shopify banner to the full set of European regions, fresh visitors correctly got the pending state and the banner, while returning visitors who had browsed before the banner existed stayed grandfathered as accepted and were never shown it. That is one store's observed behaviour rather than a documented rule, so confirm it on yours before you read an accept rate: those early weeks can mix two populations.
Checkout is a different environment. Shopify's checkout runs pixels inside that sandboxed iframe, on an opaque origin where cookie writes throw. A setup that is perfect on the storefront can still produce checkout sessions carrying no page context. That is a measurement fault rather than a consent one, but it surfaces the moment the numbers refuse to reconcile.
What to check when the numbers still look wrong
Diagnose the two systems separately, because the symptom names the side that failed. Custom Pixels still firing after a rejection means the Customer Privacy API was never written to: step four. GA4 going silent rather than sending denied pings means your defaults deny without Consent Mode active at all: step two. Cookies before any click means something loads above your defaults, usually a hardcoded script rather than a container tag, which is a categorisation question.
Velo handles both halves from one decision: the banner sits in the theme, writes Consent Mode and the Customer Privacy API on the same click, and passes the same state into the container, so the two systems cannot end up disagreeing about what the visitor said.
Common questions
What people ask about this topic.
How do I set up Google Consent Mode v2 on Shopify?
Set the Consent Mode defaults to denied in theme.liquid above your container, load your banner in the theme rather than in a Custom Pixel, and on the visitor's click send both the Consent Mode update and a call to Shopify's setTrackingConsent. Shopify runs two consent systems: the Customer Privacy API governs Custom Pixels and app pixels, and Consent Mode governs the tags in your container. Writing to only one leaves the other firing on whatever state it already held. Then verify on the live storefront on both the accept and the deny path, and check out separately, because checkout runs in its own sandbox.
Does Shopify's own cookie banner support Google Consent Mode v2?
Not on its own. Shopify's banner writes the visitor's choice to the Customer Privacy API, which is the platform side, and that correctly governs Custom Pixels and app pixels. It does not send a Consent Mode update, so the tags in your own container are unaffected by it. You still need a bridge that listens for the consent decision and issues the gtag consent update, or a consent app that does both halves for you.
Why do my Shopify Custom Pixels still fire after a visitor rejects cookies?
Because Custom Pixels are governed by Shopify's Customer Privacy API, not by Google Consent Mode, and nothing has written a rejection to it. Consent Mode has no reach into the platform layer. Whatever collects the choice has to call setTrackingConsent with the matching categories on the same click that sends the Consent Mode update. Until it does, the platform holds whatever state it had, and pending is not a rejection.
Why does the cookie banner not appear for some returning visitors on Shopify?
On one store where we expanded a Shopify banner to the full set of European regions, visitors who had browsed before the banner existed stayed grandfathered as accepted and were never shown it, while fresh visitors correctly got the pending state and the banner. That is one store's observed behaviour rather than a documented rule, so confirm it on yours. Where it holds, an accept rate measured in the first weeks after launch is mixing two populations.
Your banner, your consent,
your data, all in one place.


