Server-Side Shopify Tracking
Server-side tracking for Shopify moves event processing from the user’s browser to a server you control. This improves data accuracy (bypasses ad blockers and ITP), enables first-party cookie setting with full lifetime, and unlocks server-to-server integrations like Meta CAPI and Google Ads conversion uploads. Stape is the most accessible platform for hosting a server GTM container.
Architecture overview
Section titled “Architecture overview”User's browser ↓Shopify store (with GTM web container) ↓ HTTP requests to your sGTM endpointsGTM container (on Stape servers) ↓ Server-to-server requestsGA4 Measurement ProtocolMeta CAPI (Conversions API)Google Ads Conversion UploadThe web GTM container handles client-side event collection. The sGTM container receives those events and distributes them to GA4, Meta, and Google Ads via server-to-server connections.
Step 1 — Set up Stape
Section titled “Step 1 — Set up Stape”- Create an account at stape.io
- Create a new Server GTM Container:
- Click New Container → Server
- Stape auto-provisions a server GTM container and gives you a subdomain:
yoursite.stape.io
- Note your Container Config URL:
https://yoursite.stape.io/gtm.js - Note your Server endpoint:
https://yoursite.stape.io
Step 2 — Configure the web GTM container
Section titled “Step 2 — Configure the web GTM container”In your existing GTM web container:
-
Install the GA4 client configuration for sGTM
Open your Google Tag (the GA4 configuration) and update it:
- Expand Configuration settings → Fields to set
- Add:
transport_url→https://yoursite.stape.io - Add:
first_party_collection→true
This routes GA4 hits through your sGTM server instead of directly to Google.
-
Verify the web container sends to sGTM
In GTM Preview, fire a
page_viewevent. In the Network tab of DevTools, you should see a request going toyoursite.stape.ioinstead ofgoogle-analytics.com.
Step 3 — Configure the server GTM container
Section titled “Step 3 — Configure the server GTM container”In your sGTM container (access via the Stape dashboard or Tag Manager’s server container interface):
-
Create a GA4 Client
- Client type: GA4
- This client receives events from the web container and makes them available to server-side tags
-
Create a GA4 Tag (server-side)
- Tag type: Google Analytics: GA4
- Measurement ID: your GA4 Measurement ID (G-XXXXXXXXXX)
- Trigger: GA4 (the client)
This forwards all GA4 events from the browser to GA4 via the Measurement Protocol.
-
Create a Meta CAPI Tag (if needed)
- Install the Facebook Conversions API template from the template gallery
- Configure:
- Pixel ID: your Meta Pixel ID
- Access Token: your Meta Conversions API access token (from Meta Events Manager)
- Enable Event Deduplication: set
event_idparameter to prevent double-counting with client-side Pixel
-
Create a Google Ads Conversion Tag (if needed)
- Tag type: Google Ads Conversion Tracking
- Same Conversion ID and Label as your client-side tag
- Add user data variables for Enhanced Conversions from the server-side event data
Step 4 — Shopify-specific setup
Section titled “Step 4 — Shopify-specific setup”Shopify requires special handling for purchase events because the order confirmation page (/thank_you) is hosted on checkout.shopify.com, not your domain.
Option A — Shopify Customer Events (recommended for Shopify 2.0)
Section titled “Option A — Shopify Customer Events (recommended for Shopify 2.0)”-
In Shopify Admin → Settings → Customer events
-
Click Add custom pixel
-
Name it “GTM Server-Side”
-
Add this code:
analytics.subscribe('checkout_completed', (event) => {const order = event.data.checkout;// Push to parent page's dataLayer (for GTM web container)window.parent.postMessage({type: 'GTM_EVENT',payload: {event: 'purchase',ecommerce: {transaction_id: order.order.id,value: parseFloat(order.totalPrice.amount),currency: order.totalPrice.currencyCode,tax: parseFloat(order.totalTax?.amount || 0),shipping: parseFloat(order.shippingLine?.price?.amount || 0),items: order.lineItems.map((item, index) => ({item_id: item.variant?.sku || item.variant?.id,item_name: item.title,item_variant: item.variant?.title,price: parseFloat(item.variant?.price?.amount || 0),quantity: item.quantity,index: index}))}}}, '*');}); -
Save and activate the pixel
Option B — Shopify Order Status page injection
Section titled “Option B — Shopify Order Status page injection”For older Shopify themes:
- Go to Shopify Admin → Settings → Checkout → Order status page
- In Additional scripts, add:
{% if first_time_accessed %}<script> window.dataLayer = window.dataLayer || []; window.dataLayer.push({ ecommerce: null }); window.dataLayer.push({ event: 'purchase', ecommerce: { transaction_id: '{{ order.order_number }}', value: {{ order.total_price | money_without_currency | remove: ',' }}, currency: '{{ shop.currency }}', tax: {{ order.total_tax | money_without_currency | remove: ',' }}, shipping: {{ order.shipping_price | money_without_currency | remove: ',' }}, items: [{% for line_item in order.line_items %}{ item_id: '{{ line_item.sku | escape }}', item_name: '{{ line_item.title | escape }}', item_variant: '{{ line_item.variant.title | escape }}', price: {{ line_item.price | money_without_currency | remove: ',' }}, quantity: {{ line_item.quantity }} }{% unless forloop.last %},{% endunless %}{% endfor %}] } });</script>{% endif %}The {% if first_time_accessed %} guard prevents duplicate purchase events on page refresh.
Step 5 — Install GTM on Shopify theme
Section titled “Step 5 — Install GTM on Shopify theme”- In Shopify Admin → Online Store → Themes → Edit code
- Open
theme.liquid - Add GTM snippet in
<head>:<!-- GTM head snippet -->`{{ '<!-- Google Tag Manager -->' }}`<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script><!-- End GTM --> - Add GTM noscript after
<body>tag - Save
Step 6 — Verify the full pipeline
Section titled “Step 6 — Verify the full pipeline”- Test a purchase using Shopify’s test payment gateway
- In GTM Preview (web container), verify the
purchaseevent fires with correct ecommerce data - In GTM Preview (server container), verify the event is received by the GA4 client
- Verify the GA4 server tag fires and forwards to GA4
- Verify Meta CAPI tag fires (if configured)
- In GA4 DebugView, verify the purchase event appears with correct revenue and items
- In Meta Events Manager → Test Events, verify the Purchase event appears
Cookie setup for first-party tracking
Section titled “Cookie setup for first-party tracking”Set up Stape’s first-party cookie proxy to write GA4 cookies directly on your Shopify domain, bypassing ITP restrictions:
- In Stape → Settings → Custom Domain
- Add a subdomain of your Shopify store:
gtm.yourshop.com - Point it at your Stape endpoint via CNAME
- In your Google Tag configuration, update
transport_urlto use your custom domain
First-party cookies set this way have a full 13-month expiry instead of the 7-day maximum Safari ITP imposes on script-set cookies.
Test it
Section titled “Test it”- Complete a full test purchase in Shopify
- Verify in GA4:
purchaseevent appears in DebugView with correcttransaction_idandvalue - Verify in Meta Events Manager:
Purchaseevent received with matchingevent_id - Verify no duplicate events (check
transaction_idappears only once in GA4 Explore)
Common gotchas
Section titled “Common gotchas”Double purchase events. If you have client-side tracking AND server-side tracking both configured to send purchase, you will get duplicates. Disable the client-side GA4 purchase event and let sGTM handle it exclusively. Use event_id deduplication for Meta CAPI.
checkout.shopify.com is a separate domain. Shopify’s checkout runs on a Shopify-owned domain. Your GTM web container is not loaded there (unless you are on Shopify Plus). Use Customer Events or the Order Status injection method for purchase tracking.
Stape server location affects latency. Choose a Stape server region close to your primary user base. If most of your customers are in Europe, do not host the sGTM container in US-East.