
GA4 consent-based conversion tracking on Shopify with Custom Pixels, Cookifi & GTM
In this guide, we’ll walk through how to track Google Analytics 4 (GA4) events on a Shopify store using Custom Pixels and Google Tag Manager (GTM).
By the end of this guide, you’ll know how to:
implement end-to-end e-commerce tracking across both the storefront and checkout
set up consent-based tracking that works reliably with Google Consent Mode v2 using Cookifi. If you’re looking for a reliable, freelancer-focused CMP that’s easy to integrate with GTM, check it out - we’re currently offering special deals for early adopters.
This setup works for both Shopify Plus and non-Plus plans (Basic, Shopify, Advanced).
The guide contains ready-to-use code snippets that you can just copy and use. At the end of the guide, you’ll also be able to get free GTM templates for either Basic or Advanced Google Consent Mode configuration.
—
This article provides technical guidance only and does not constitute legal advice. Implementing any of the tracking methods or configurations discussed is solely your responsibility. You should consult a qualified legal professional to ensure compliance with GDPR, CCPA, or any other applicable privacy regulations based on your specific situation. Always verify that your implementation aligns with legal requirements in your jurisdiction.
—
Outline
Business context
Before we dive in, here’s a quick look at the business setup:
the business is an e-commerce store selling medical wear
the simplified customer journey looks like this:
Platform context
Shopify has deprecated checkout scripts and other older tracking methods.
Despite the limitations, the recommended and future-proof way to track customer interactions is through Customer Events, using Custom Pixels.
That’s what we’ll use in this guide.
What you’ll need
This guide assumes that:
you already have your GTM container and GA4 property set up
you’re familiar with navigating GTM and GA4
(optional) you have a Cookifi property configured
you have Shopify theme code editor & Customer Events access
Clean up existing tracking
First, make sure no conflicting GA4 tracking is present. Most Shopify stores - unless brand new - have legacy setups using the Google & YouTube Sales Channel or Additional Scripts.
These need to be removed to avoid duplicate tracking.
How to check
GTM’s Preview Mode won’t work for Custom Pixels, since they run inside a sandboxed iframe.
Instead, use Chrome DevTools:
Open DevTools > Network
Filter requests using collect?v=2
Look for GA4 events (e.g., page_view, purchase, etc.)
If GA4 requests are present before your new setup, find the source and remove/disable it.
Repeat until your site is clean with no GA4 events firing.
How it works
Once legacy tracking is removed, here’s a simplified overview of how the new setup works:
The cookie banner appears and prompts the visitor for consent
Cookifi loads the consent data and passes them to Shopify’s backend via the Customer Privacy API
When users interact with the store, Shopify emits Customer Events (checkout_completed, page_viewed, etc.)
A Custom Pixel subscribes to those events and retrieves the data
The same pixel also listens for any consent updates
The event and consent data are pushed to the data layer
GTM reads the data layer and forwards events to GA4 (or any other tool)
Consent enforcement happens at the tag level inside GTM - our setup ensures the right data reaches the data layer, and GTM decides what fires based on consent, giving you fine-grained control over the tracking and consent handling.
Don’t get too excited though - there are limitations. We’ve listed known limitations in this article - don’t skip those to verify this setup is right for you.
Consent-related limitation:
The cookie banner will only load and appear on storefront pages (not checkout pages). However, I’ve added a workaround to still retrieve consent signals reliably even on checkout.
If you’d rather have me take care of this for you, feel free to reach out here — happy to help.
If you're looking for data layers for Google Ads (with customisable product IDs, and dynamic remarketing), Meta Ads, and others - please reach out here, I've built solutions for those too.
Theme files configuration
Download the code files
First, download the code files from this GitHub repository.
As you’ll notice, it contains the following files:
theme
cookifi
gtm-customer-events-storefront
custom pixel
I’ll walk you through where each script goes and how it should be customised.
Configure and integrate the cookie banner
In your Cookifi property, disable the Google Consent Mode Send Update Command for all regions:
The default and update commands will instead be handled from within the Custom Pixel itself.
Then, go to General > Integration script and copy the provided script:
Don’t generate the Default Consent Mode command just yet - we’ll do that later.
Theme.liquid file
In Shopify, go to Online Store > Themes > Edit code, and search for the theme.liquid file.
Paste your Cookifi integration script at the very top of the <head> element:
Shopify will prompt you to add defer - go ahead and add that to the <script> tag:
This ensures the banner loads on all storefront pages.
Even though the banner won’t load on checkout, we will still retrieve and pass consent data via a workaround covered later.
Now search for content_for_header and paste the contents of the theme file that you downloaded from GitHub right below it:
Shopify will warn you that these snippets don’t exist - that’s fine, we’ll create them next.
Add the required snippets
Now let’s create the two snippets you referenced in theme.liquid.
Within the code editor, locate the snippets section and click Add a new snippet:
Snippet 1: Cookifi
Snippet name: cookifi
Open the cookifi file you downloaded from GitHub and paste its contents into the newly created snippet:
This snippet ensures Cookifi consent is loaded and synced with Shopify’s Customer Privacy API, making it accessible to Custom Pixels.
Snippet 2: Customer Events storefront
Snippet name: gtm-customer-events-storefront
Open the gtm-customer-events-storefront file that you downloaded from GitHub and paste its contents into the newly created snippet:
This snippet improves click tracking on storefront pages by publishing the data as custom Customer Events, which we’ll subscribe to later in our Custom Pixel.
With your theme and snippets now set up:
the Cookifi cookie banner gets loaded
Cookifi retrieves & applies the visitor’s consent
consent is synced with Shopify’s backend
click events on storefront pages are captured and published as custom Customer Events
Now you’re ready to configure the Custom Pixel.
Customer Events
Here’s the list of standard Customer Events available. Feel free to familiarise with the available events and their data structure.
For this guide, we’ll use the following standard events:
Shopify standard Customer Event - event name pushed to the data layer
page_viewed - page_view
clicked - click
search_submitted - view_search_results
form_submitted - form_submit
collection_viewed - view_item_list
product_viewed - view_item
product_added_to_cart - add_to_cart
cart_viewed - view_cart
product_removed_from_cart - remove_from_cart
checkout_started - begin_checkout
checkout_shipping_info_submitted - add_shipping_info
payment_info_submitted - add_payment_info
checkout_completed - purchase
We’ll also use the two custom events that we published earlier using the snippets:
Shopify custom Customer Event - event name pushed to the data layer
custom_click - custom_click_storefront
custom_link_click - custom_click_link_storefront
Custom Pixel
Now let’s set up the Custom Pixel that will subscribe to these events, collect data, and push it to the data layer.
Add new Custom Pixel
Navigate to Shopify Settings > Customer Events and click Add custom pixel:
Name the pixel, e.g. GTM, and click Add pixel.
Customer privacy settings
Under Customer privacy, choose:
Permission: Not required
Data sale: Data collected does not qualify as data sale
Why? Because this pixel only pushes data to the data layer - it doesn’t send anything directly to third parties. Consent is enforced at the tag level within GTM.
Script customisation
Open the custom pixel file that you downloaded from GitHub and paste its contents inside the newly created pixel.
Inside the script, you’ll see a section called Global settings at the top:
That’s the only place in the code where you’ll want to customise:
the conversionTracking object:
make sure to replace the placeholder GTM container ID with your actual one
optionally, you can disable any event you don’t want to push to the data layer by setting its value to false
the consent object:
waitForUpdate - defines how long to wait for the update command before firing tags. While 500ms is usually enough, you may need to increase it if you notice events being sent before the update command has time to load. This could result in incorrect consent signals (e.g., if cookies are accepted but the page_view event is sent with a gcs=G100 parameter, it likely means the default command was used instead of the update).
adsDataRedaction - if set to true, ad-related data will be redacted further when ad_storage is denied
urlPassthrough - if set to true, ad click information is preserved across pages via URL parameters when ad_storage is denied, increasing the chances of attribution until the user provides consent.
the developer object:
enabling this will show logs in the DevTools console to help with troubleshooting and debugging
Google Consent Mode
As you may know, Google Consent Mode relies on two commands - default and update. These commands control how tags behave and whether they’re allowed to fire in GTM, depending on the selected mode (Basic or Advanced).
The Custom Pixel script handles both commands.
The default command uses hardcoded denied values for all storage types, except for security_storage, which remains granted by default:
The update command uses the actual visitor’s consent choice via Cookifi:
Optionally, if you want to collect more data from non-EEA visitors where legal, you can make the default command region-specific, which is very straightforward to do in Cookifi.
Go to your Cookifi property > Regions and click the plus button to create a new region:
Search for relevant country, or ‘eea’ for the EEA region, and click Create:
Inside the newly created region, make sure the storages for analytics, marketing and preferences are set to denied, and the Send Update Command is disabled:
Then, go back to your Global region, and set the storages to granted (if legally acceptable for your visitors from other countries):
Save the configuration and go to General > Integration script, and make sure to click on the Google icon:
Clicking on the icon generates the Default command code based on your configured regions. Since our Custom Pixel already handles some parts of that code, make sure to only copy what’s highlighted below:
Go to your Custom Pixel code, and replace the existing default command with the new code, so it looks like this:
Make sure to NOT replace the ads_data_redaction and url_passthrough!
Thanks to this, Google itself will determine the correct default command to apply, based on the visitor’s location. As a result, EEA visitors will get strict consent defaults, while others will get a more permissive setup.
And that’s it! Once happy with your customisations, go ahead and save the pixel. Make sure to connect it to make the pixel live.
Test
Time to make sure everything works as expected!
Since testing is a bit trickier with Custom Pixels, we’ve already put together a detailed guide on how to test your setup on Shopify - be sure to check it out.
Under the hood
This section is for those curious about the technicalities of this setup. If you’d prefer to skip the deep dive, feel free to jump straight to the GTM configuration.
The challenge
When a new visitor lands on your Shopify store, the cookie banner appears, prompting them to make a consent choice:
At the same time, standard Shopify Customer Events - such as page_viewed - may fire immediately.
Since the Custom Pixel is configured with permissions not required, it executes immediately, subscribing to and capturing these events regardless of consent status.
Here’s the problem:
We need to ensure that:
Consent data is available and correct
Customer Events are only pushed to the data layer after the consent has been loaded and applied
On the first visit, page_viewed and other events usually fire before consent is collected. On later visits, the opposite may happen. So we’re dealing with a race condition: the order of consent retrieval vs. event firing is unpredictable.
To avoid incorrect tracking or firing tags prematurely, we must delay sending events to the data layer until the user’s consent has been obtained (regardless of whether it’s granted or denied).
The solution
The approach I’ve taken solves the race condition in a clean and robust way.
The code uses:
Shopify’s analytics.subscribe(<eventName>) to listen for Customer Events
visitorConsentCollected event to know when consent from Cookifi has been obtained and sent to Custom Pixel
But instead of pushing event data directly to the data layer as soon as it’s captured, we first check if consent is available:
If consent is ready: we push the event to the data layer immediately.
If consent is not yet available: we store the event in a temporary holding object.
Once consent is received via visitorConsentCollected, we:
Retrieve all queued events from the temporary object
Attach the correct consent signals
Push them to the data layer for GTM to process
This ensures events are sent with the correct consent values, regardless of which (event vs consent data) has been received first.
Another challenge
The above solution works great on storefront pages, where the Cookifi banner loads normally and can emit the consent status.
However, on checkout pages, the banner doesn’t load (as mentioned in the limitations), which means:
No consent update gets sent to Custom Pixel
No visitorConsentCollected event is triggered, making this hook unusable on checkout pages
While there is an init.customerPrivacy object available, it’s… tricky to say the least.
If the visitor hasn’t made any consent choice yet (i.e., they land directly on a checkout page), this object defaults to true, meaning tracking is enabled by default - even if consent hasn’t been given.
That’s a risky default.
To avoid this, we retrieve consent signals from Shopify’s "_tracking_consent" cookie instead. Unlike init.customerPrivacy, this cookie doesn’t default to true when no prior consent has been set.
If the user hasn’t made a choice yet, the values for each cookie category (analytics, marketing, preferences) will be empty strings, and sale of data will be set to 0:
We can interpret these values as “denied” and only use granted values when they’re explicitly set:
Due to this reason, checkout pages are treated differently:
On storefront pages: we wait for visitorConsentCollected
On checkout pages: we parse the "_tracking_consent" cookie directly
This hybrid method ensures tracking logic is privacy-safe across both contexts, and also covers edge cases.
GTM configuration
Now all you’re left to do is configure GTM tags, triggers, and variables :)
If you wish to save time on configuring the GTM container, you can download my free GTM container templates (for both Advanced and Basic Consent Mode) that work seamlessly with this setup.
If you’re unsure or need more information about Google Consent Mode, you can always refer to this comprehensive Cookifi documentation.
Please note for click tracking, this template includes only the essential triggers and Data Layer variables. You’ll still need to set up specific click tracking configurations tailored to your needs.
To download the templates, please click here.
If you’d rather have me take care of this for you, feel free to reach out here — happy to help.
If you're looking for data layers for Google Ads (with customisable product IDs, and dynamic remarketing), Meta Ads, and others - please reach out here, I've built solutions for those too.