The problem
The Goody Bar, a Shopify retailer investing more than £1 million a year in paid media, had a gap between Shopify orders and the purchases visible to its analytics and advertising platforms. Its client-side setup depended on browser events through checkout, where consent choices, browser restrictions and the Shopify checkout sandbox could interrupt attribution data.
The team wanted to know whether server-side Google Tag Manager could improve purchase capture without inflating reported conversions or breaking channel-level revenue comparisons.
A higher event count is not a result. Orders and revenue that line up by channel are.
What I found
Before migrating anything I inventoried every path that could emit a purchase. The existing setup had several overlapping ones:
- Two GTM loads. The Stape app and an older theme snippet both loaded GTM.
- Google & YouTube app. It sent Google Ads conversions independently of the container.
- Facebook & Instagram app. It sent Conversions API events alongside the proposed server-side Meta tag.
Checkout is its own environment
The Custom Pixel could not read storefront cookies directly, so the Google click ID (gclid) had to be collected through Shopify’s Web Pixels API and carried with the purchase. Checkout also reported a web-pixel URL rather than the customer-facing page URL unless that value was passed through explicitly.
A Stape subdomain doesn’t fix Safari on its own
For this setup the storefront and the server endpoint used different IP ranges, so Safari’s seven-day cookie limit still applied to returning visitors. The fix is a first-party cookie strategy that matches how the store is routed, not just a custom domain.
What I built
The server-side implementation ran in parallel with the existing client-side one and reported to a separate GA4 property, so both could be compared against the same Shopify orders over the same window. Nothing live was replaced to get the data.
- Removed the duplicate GTM load.
- Disabled the overlapping Google Ads purchase send for the length of the comparison, so one path owned each conversion.
- Gave Meta browser and server events a shared
event_idso they deduplicate. - Passed
gclidand the real page URL through the Web Pixels event payload at checkout. - Reviewed Safari returning-visitor behaviour and documented the same-origin routing requirement for the store’s more complex setup.
Shopify storefront + checkout
Web Pixels API gclid + real page URL on the purchase payload
|
+--> client-side GTM --> GA4 property A (existing setup)
|
+--> server-side GTM --> GA4 property B (parallel run)
--> Meta CAPI (event_id shared with browser)
Source of truth Shopify completed orders, by channel and revenue
Simplified topology, not a config export.
The reconciliation dashboard
Purchase events by week, Google Ads conversions, a reconciliation table against Shopify orders, a channel breakdown and the implementation checks. The period filter is live — try it.
How it was judged
Both implementations were compared with Shopify’s completed orders, by channel and by revenue. A few rules kept the comparison honest:
- Shopify is the source of truth for completed orders; GA4 is measured against it, not against the other GA4 property.
- Filter before you celebrate. Thirty suspected bot or invalid purchase events were removed from the raw server stream before any gain was evaluated.
- Platform numbers are attribution, not incrementality. More Google Ads conversions after the change is a measurement difference, not proof of extra sales.
- Event count is not the cutover criterion. Cutover waits on transactions and revenue aligning by channel across the agreed observation period.
Measurable outcome
Across the eight-week comparison, Shopify recorded 4,280 orders. The client-side GA4 property observed 3,768 purchase events. The parallel server-side property recorded 4,255 raw events; after filtering 30 suspected bot or invalid events, 4,225 qualified purchases remained. That is 12.1% more qualified purchases than the client-side property, and 98.7% of Shopify’s order count.
In Google Ads, 1,000 client-side conversions compare with 1,124 qualified server-side conversions, a 12.4% increase. Reported attributed revenue moved from £780,000 to £875,000. These are platform-reported attribution figures, not incremental sales caused by the tracking change.
Where the remaining gap sits
The 55-order gap to Shopify, by channel (Shopify orders / client-side / server-side):
- Google Ads — 1,180 / 1,000 / 1,124
- Paid social — 700 / 600 / 675
- Organic & direct — 1,760 / 1,550 / 1,725
- Other — 640 / 618 / 701
The channel totals reconcile to the overall counts above.
The controls behind the comparison
The duplicate GTM load was removed; checkout page paths were corrected for 98.7% of sampled events; gclid was present on 94.0% of eligible checkout purchase events; and shared Meta event_id deduplication was in place.
Cutover decision
The results support a final cutover review, but the higher event count alone does not pass the gate. Cutover remains pending until transactions and revenue align by channel across the agreed observation period, with bot filtering and platform attribution settings reviewed alongside the event totals.
Moving a Shopify store to server-side tracking?
Start by listing every source that can send a purchase — apps, theme code, pixels, GTM containers and server-side destinations — then run the server container in parallel and reconcile transactions and revenue by channel before you cut over.