Server-side tracking for Shopify with the Measurebase app
Shopify is where normal tagging setups quietly fall apart. The checkout is a locked environment where you can't place your own scripts, pixels run inside a sandbox that reports the wrong page URL, and by the time a payment provider has bounced the shopper around, the session and its attribution are often gone. The Measurebase Shopify app exists to close exactly those gaps: it connects your store to your own Measurebase tagging host and tracks the whole funnel server-side · storefront, checkout and the order itself.
Three surfaces, one stream
The app tracks your store from three places at once, and every one of them reports to the same tagging host with the same identifiers:
- Storefront · theme embed. One toggle in your theme editor. It pushes the standard GA4 e-commerce events into the dataLayer for your web GTM container, loaded through your own first-party tagging host.
- Checkout · app pixel. Installed automatically with the app · no code to paste. It covers the whole checkout funnel from inside Shopify's checkout, where your own scripts can't go.
- Orders · server webhooks. The order itself, reported server-to-server: purchases, refunds with a correct full-or-partial classification, and (optionally) subscription orders. This surface keeps working when the browser never got the chance · an app-blocked device, a killed tab, a payment app that never returned the shopper to your site.
Every event the app sends
All events use GA4's standard names and shapes, so they work with your existing tags, audiences and reports without translation. Storefront events, from the theme embed:
| Event | Fires when |
|---|---|
view_item | A product page is viewed · with the selected variant's price |
view_item_list | A collection or search results page is viewed · items with list name and position |
select_item | A product is clicked in a collection or search list · works across theme card layouts, including themes that don't wrap the card in a link |
add_to_cart | Any add to cart · quantity is the amount just added, never the running cart total |
remove_from_cart | Any cart removal or quantity decrease · computed by diffing the cart |
view_cart | The cart page is viewed, with the full cart contents |
search | A search results page is viewed · with search_term and search_results |
| Your custom events | Anything published on Shopify's analytics bus · a quiz, a wishlist, an app's event · mirrored into the dataLayer under a name you choose |
Checkout events, from the app pixel:
| Event | Fires when |
|---|---|
begin_checkout | The checkout is opened |
add_shipping_info | The shipping step is completed |
add_payment_info | The payment step is completed |
apply_discount_code | A discount code is applied · with the code and the discount amount |
purchase | The order is placed · event ID is the order ID, replays of the thank-you page are suppressed |
nc_purchase / rc_purchase | Optional companions to the purchase for new and returning customers · never counted as a second conversion, only emitted when Shopify actually knows the segment |
Server events, from order webhooks:
| Event | Fires when |
|---|---|
purchase | The order is created · as the owning event or as a deduplicated copy, depending on the purchase mode you choose below |
refund | A refund is issued · classified full or partial by comparing the refunded amount against the order total, so a shipping-only refund never reverses the whole order |
subscription_started | A subscription's first order is placed (opt-in) |
subscription_renewed | A recurring subscription order is billed (opt-in) · as an annotation event, or as a countable purchase if you want renewals in revenue |
Every parameter, and why it's there
The e-commerce object
Every commerce event carries the full GA4 e-commerce object: currency, value, transaction_id, tax, shipping, coupon and order-level discount, plus an items array with item_id, item_name, item_brand, item_category, item_variant, price, quantity and per-item discount. The item ID source is configurable · product ID, variant ID, SKU or barcode · and the same choice applies on every surface, so your product feed matches from the first view_item to the server-side purchase. Money is always a clean number, never a formatted string and never floating-point noise.
Identity and matching
Events carry customer identifiers in Google's user-provided data schema, and only ever as SHA-256 hashes · plain email, phone or address never enter the dataLayer and never leave the store:
user_data.sha256_email_addressandsha256_phone_numberuser_data.addresswith hashed first name, last name and street, plus plain city, region, postal code and country (the schema requires geography plain · there is no hashed form for it)user_id· the Shopify customer ID for signed-in shoppers, which turns on GA4's cross-device User-ID
Identity is seeded into the dataLayer before GTM loads, on the storefront and in checkout alike · so the Google tag's very first hit already carries the user ID and the match keys, instead of only the events that happen to fire after everything has settled. In checkout, hashes refresh on every step as the shopper fills in more fields, so advanced matching doesn't wait for the purchase.
Click IDs that reach the conversion
Google, Microsoft, Meta and TikTok click identifiers · gclid, gbraid, wbraid, msclkid, ttclid, plus the _fbp/_fbc cookies · are captured on the landing page, stamped onto the cart, and forwarded with checkout and server events. The iOS variants gbraid and wbraid matter most: they exist precisely for the visits where every other identifier has already been stripped, and most setups drop them entirely.
Context on every event
mb_marketandmb_language· which Shopify Markets storefront, and the language the shopper actually chose (browser language alone can't tell you that on a multilingual store)logged_inandcustomer_type· signed-in state as an explicit true/false on every storefront event, and new-versus-returning derived from real order history · the two dimensions audience building actually needspage_locationandpage_referrer· the real page URL, also inside checkout, where the pixel sandbox would otherwise report Shopify's internal worker address as the page- Server events add the session's landing page with its UTMs intact, the original referrer, and the buyer's user agent and IP · so a server-side purchase still gets device and geo instead of looking like a data-center hit
Bookkeeping that keeps the counts honest
event_id· the order ID on purchase, identical on all three surfaces · the key GA4 and every ad platform deduplicate onmb_checkout_token· joins a browser session to its webhook order, so the server copy carries the browser's identitynew_customer· on the purchase itself, under the standard name Google Ads' new-customer acquisition bidding readsmb_countable· explicitly zero on companion and identity events, so nothing downstream can mistake an annotation for a second conversionmb_purchase_ownerandmb_source· which surface owns the purchase and which one sent each event, so any number can be audited back to its origin
One purchase, never two
With three surfaces able to see the same order, the thing that matters most is that your reports only ever count it once. The app gives every purchase one event ID · the order ID itself · across all three surfaces, and lets you choose who owns the purchase:
- Browser · the checkout pixel reports the purchase, the webhook stays quiet.
- Browser + server · the pixel reports it and the webhook sends a server-side copy carrying the same event ID and the same browser identity, so GA4 and every ad platform deduplicate cleanly while you gain the reliability of a server event.
- Server · the webhook owns the purchase entirely; the browser only contributes identity. The strongest option against blockers, since the conversion never depends on the shopper's browser at all.
Reloaded thank-you pages, double-pasted pixels and replayed webhooks are all suppressed · defence in depth on every surface, not an assumption that nothing ever goes wrong.
Why this is such a strong setup
Each piece above is useful on its own; the strength is what they add up to. A few claims most Shopify tracking can't make, and why this setup can:
- The conversion doesn't depend on the shopper's browser. Ad blockers, ITP, a killed tab, an in-app browser that never returns from the payment provider · none of them can erase a purchase, because the order webhook reports it server-to-server with the browser's identity joined on via the checkout token. Pixel-only setups lose exactly these orders and never know it.
- Redundancy without double-counting. Most "belt and braces" setups quietly inflate revenue, because their belt and their braces each report the sale. Here every surface uses the order ID as the event ID and marks who owns the purchase, so you get the reliability of three reporters with the arithmetic of one.
- Match keys arrive with the event, not after it. Enhanced conversions and CAPI-style matching live or die on whether the hashes are present on the hit. Identity is seeded before GTM boots and refreshed at every checkout step, so even the first page view of a signed-in shopper carries a match key · and the purchase always does, from whichever surface delivers it.
- Attribution survives the hardest paths. The chosen language, the Markets storefront, the real checkout page URL, the landing page with UTMs intact, and the full click-ID set including iOS's
gbraid/wbraid· the paths where attribution usually degrades into "direct / none" keep their source here. - First-party end to end, EU-hosted. Storefront container, checkout traffic and server events all flow through your own subdomain on EU infrastructure · with Enhanced Ad Blocker Protection extending into checkout, so even path-based filter lists have nothing stable to match.
- Consent-aware by construction. The shopper's consent state travels with the events, and identifiers are hashed at the source rather than "somewhere later" · the setup is strong because it is conservative with data, not despite it.
- Nothing is a black box. Every event, every parameter and every product on every hit is inspectable in the app's Test tab, and every count can be traced to the surface that produced it. When a number looks odd, you can find out why · not guess.
Built for your GTM setup, not around it
You keep your own web and server containers. The dashboard generates a ready-to-import GTM template in Shopify mode: every e-commerce field mapped explicitly (the checkout sandbox ignores the usual "read from dataLayer" shortcut), a user-provided-data variable wired to the hashed fields, and the real page URL mapped in so checkout events report the checkout page rather than Shopify's internal sandbox address.
Shopify Markets is handled per storefront: a shop with multiple domains tags each one through its own host, resolved automatically · one app, one pixel, every market first-party.
Anything your store publishes through Shopify's own analytics events · a quiz completing, a wishlist add, an app's custom event · can be mirrored into the dataLayer under a name you choose, from the app's Events tab. And Enhanced Ad Blocker Protection extends into the checkout: container traffic leaves masked there too.
Seeing what's happening
The app's Test tab shows every event that reached your tagging host in the last 30 minutes · which surface sent it, its value, its products item by item, and every parameter it carried, including traffic that left through the ad-blocker mask. A console debug mode and a server-preview header are one toggle away, and both switch themselves off automatically after 24 hours, so a debugging session can't silently degrade your production data. The Setup page continuously checks each piece · embed, pixel, webhooks · and tells you when something needs attention.
Getting started
- Install the Measurebase app on your store and connect it to your Measurebase domain.
- Turn on the theme embed in your theme editor for storefront events.
- Import the generated GTM template from your dashboard, or map the events into your existing containers.
The checkout pixel installs itself with the app, and order webhooks are registered automatically. From the first page view to the purchase and its refund, everything flows through your own domain · first-party, deduplicated and EU-hosted like everything else on Measurebase.