Shopify's built-in cookie banner doesn't block theme scripts
Guide · 7 August 2026 · Novece
If you switched on Shopify's cookie banner and then watched your GA4 tag fire before anyone clicked anything, you are not misreading the network tab. The banner is working. Its job is just narrower than almost everyone expects it to be.
That gap matters because of why the banner went on in the first place. Under GDPR and ePrivacy the point of a consent notice is that analytics and advertising scripts wait for the visitor's answer. A GDPR cookie banner that shows the notice but lets the tag run anyway has done the visible half of the job and not the mechanical half.
The confusion comes from two different things being called "the cookie banner". This page separates them, shows you how to tell in two minutes which situation your own store is in, and gives you the free fixes for the gap.
The short version. Shopify's built-in banner records the visitor's decision in the Shopify Customer Privacy API. Everything that reads that API respects the decision — Shopify's own pixels, Custom Pixels, checkout. A script pasted into your theme never reads that API, so nothing stops it from running. That is the entire gap, and both fixes below close it without paying for anything.
What the built-in banner actually does
Turn it on under Settings → Customer privacy and it does three things: it shows the notice, it records the visitor's decision, and it writes that decision into the Customer Privacy API.
The third one is the one that matters. The Customer Privacy API is the channel Shopify's own surfaces read consent from. Shopify describes the decision as applying to Shopify-managed surfaces — its own pixels, audiences and checkout. Add a Custom Pixel under Settings → Customer events and it reads the same API, so it honours the same decision.
Inside that boundary the built-in banner does what it says. If everything you measure already runs through Shopify's pixel manager, you may not need anything else at all — and it is worth checking that before you go shopping.
What it can't do, and why
Now the part that surprises people.
Take a GA4 or Meta Pixel snippet pasted into theme.liquid,
or injected by a theme, or written into your theme by an app you
installed a year ago. That is a <script> tag sitting
in your page's HTML. The browser reaches it while loading the page and
runs it. The banner has no mechanism to intervene — it is not a proxy,
it does not rewrite your theme, and it cannot un-run a script the
browser has already executed.
So the real sequence on a store like that is:
- The page starts loading.
- The theme-embedded GA4 tag runs, sets its cookies, sends its first hit.
- The banner appears.
- The visitor clicks Decline.
- The decision is recorded, and honoured by everything that reads the API. The GA4 tag ran four steps ago.
This is also why you will find forum threads where two merchants test the same setting and reach opposite conclusions. One has analytics running through Custom Pixels and sees it correctly blocked. The other has it hardcoded in the theme and sees it fire regardless. Both are describing their own store accurately, which is what makes the disagreement so hard to settle in the abstract.
Check your own store in two minutes
You don't have to guess which case you're in. There is no app and no sign-up involved in finding out.
- Open your storefront in a private window, from a region where the banner appears. Don't touch the banner — not Accept, not Decline, not the close button.
- Open DevTools (
F12) → Application → Cookies → your domain. - Read the list before you interact with anything on the page.
What you're looking for are cookies only analytics or advertising would
set. _ga and _ga_* are Google Analytics, and
so is _gid. _fbp is Meta. _ttp
is TikTok. _hjSession* is Hotjar.
Shopify's own cookies will be sitting there too and that is expected —
_shopify_y, _shopify_s, cart,
_secure_session_id, and _tracking_consent,
which is the consent record itself. Those are not the ones to look at.
If _ga or _fbp is already there before you
clicked anything, that tag is outside the banner's control.
The second check tells you which script, which is what you need before you can fix it:
- Same private window, DevTools → Network, then reload the page.
- Filter for
collect,gtm,fbeventsorgtag. - Anything that fires before you click the banner is a tag the banner isn't gating.
Click one of those requests and read the Initiator column. It names the file that started it, which is usually a line in your own theme.
Free fix 1: move the tag into Custom Pixels
If the check found hardcoded tags, this is the cheapest correct fix and it needs no app at all.
Go to Settings → Customer events → Add custom pixel, put the tag there instead of in your theme, and remove it from the theme. A Custom Pixel runs in a sandbox that reads the Customer Privacy API, so from that point the built-in banner does gate it.
Two things to know before you move everything across:
- Custom Pixels run in a sandboxed iframe and cannot touch your storefront's DOM. A tag that only measures is a good candidate; a script that changes what the page looks like is not.
- Remove the theme copy in the same change. Leaving both means the tag fires twice — once gated, once not — and the resulting numbers are wrong in a way that is genuinely tedious to trace back.
For GA4 specifically, the Google & YouTube channel app installs it in a consent-aware way as well, which is less work than hand-writing a pixel.
Free fix 2: wire Google Consent Mode v2 yourself
This is the other half, and it is worth being precise about, because Shopify's banner does not do it for you.
Consent Mode is a Google mechanism. It needs
gtag('consent', 'default', …) to run before the
Google tag loads, and gtag('consent', 'update', …) when
the visitor decides. Shopify's banner emits neither. The Shopify
Customer Privacy API applies the visitor's decision to Shopify-managed
surfaces; it does not translate that decision into gtag consent
commands for a Google tag you installed yourself.
That is why the usual symptom is a granted update appearing after someone accepts, with no default before it at all.
You can wire it up by hand. Three parts, and the third is the one most examples leave out.
1. Set the default, above your Google tag
In theme.liquid, inside <head>, above
your Google tag:
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
wait_for_update: 500
});
</script>
Order matters here. If this runs after the Google tag it has no effect at all.
2. Update when the visitor decides
Shopify fires visitorConsentCollected on
document when a choice is made:
<script>
function applyConsent(marketing, analytics) {
gtag('consent', 'update', {
ad_storage: marketing ? 'granted' : 'denied',
ad_user_data: marketing ? 'granted' : 'denied',
ad_personalization: marketing ? 'granted' : 'denied',
analytics_storage: analytics ? 'granted' : 'denied'
});
}
document.addEventListener('visitorConsentCollected', function (e) {
applyConsent(e.detail.marketingAllowed, e.detail.analyticsAllowed);
});
</script>
3. Handle returning visitors — this is the part that bites
visitorConsentCollected only fires when consent
changes. A visitor who accepted last week arrives with a
stored decision and the event never fires, so your update never runs,
and everything stays denied for somebody who already consented. Read
the stored decision on load instead:
<script>
document.addEventListener('DOMContentLoaded', function () {
var cp = window.Shopify && window.Shopify.customerPrivacy;
if (!cp) return;
var c = cp.currentVisitorConsent();
if (c.marketing === '' && c.analytics === '') return; // undecided: stay denied
applyConsent(c.marketing === 'yes', c.analytics === 'yes');
});
</script>
How to check the wiring works
-
Private window, don't touch the banner. In DevTools →
Console, run
dataLayer.filter(function(x){return x[0] === 'consent'}). You should see one default entry with everything denied, and no update. - Click Accept and run it again. Now there should be a second entry: an update, granted.
- Close the window, open a fresh private one, accept, then reload. After the reload you should still see a granted update. If you don't, part 3 isn't wired up.
One thing this does not change: Shopify's own pixels and checkout keep reading consent from the Customer Privacy API exactly as before. All you have done is connect your own Google tags to the same decision.
If your tags run through Google Tag Manager rather than a tag pasted in the theme, GTM has its own consent settings and they can override what you expect. A common one to check first is Override Consent Mode Defaults left switched on, which quietly breaks conversion tracking even for visitors who accepted.
What about apps that ignore consent?
Worth being straight about a limit that applies to every consent tool, including ours.
An app that loads its own script from its own domain, and never reads the Customer Privacy API, will carry on doing that. A banner can gate what it can see and what it has been told about: tags you configure, scripts you point it at, and surfaces that read the consent API. It cannot reach inside another app's bundle and change what that app decides to do.
So the practical order is:
- Inventory what is actually loading, using the two-minute check above.
- For each thing you find, work out whether it reads consent — a Custom Pixel and a Shopify-managed surface do, a theme-embedded tag does not, and a third-party app is a question for its own documentation.
- For an app that doesn't, the fix lives in that app's settings or with its developer, or in removing it. No banner substitutes for that conversation.
Anyone who tells you their banner blocks everything on any store hasn't described a mechanism. Ask what it does about a script it was never told about.
Where an app fits, and where ours does
Everything above is free and needs no app. It is worth saying plainly where a tool does and doesn't earn its place.
You don't need one if all your tracking already runs through Custom Pixels and you have no need to keep a record of consents. One starts being worth it when you have tags you can't move into Custom Pixels, or you'd rather not maintain Consent Mode wiring in theme code through every theme update, or you need a consent log you can produce later.
Novece – GDPR Cookie Consent is the app we build, and its free plan covers the gap this page is about:
- the cookie banner, with unlimited page views
-
real script blocking — scripts you tag are held as
type="text/plain"until consent, then swapped in and executed in their original order, so a GA4 config still runs before its events - Shopify Customer Privacy sync
- the banner in your store's language
No card, no time limit, and no page-view ceiling on the free plan.
The paid plan, Complete, is $9.99 a month or $89 a year with a 14-day trial, and adds Google Consent Mode v2, a consent log with CSV export, five visitor languages, and a cookie policy generator.
The listing is at apps.shopify.com/cookie-consent-9.
The caveat that applies to every tool on this page, including ours. This is a guide to a technical gap, not legal advice, and no app makes a store compliant on its own. What software can do is make the store behave the way you intended — which is a prerequisite, not a substitute, for the rest of it.