A Shopify store owner showed me a Google Ads account reporting 212 purchases in a month. The Shopify admin showed 287 orders, and roughly two thirds of traffic came from Google Ads. Nothing was broken in the usual sense; the browser tag fired perfectly on every thank-you page it was allowed to load. The missing orders were customers on Safari, customers who declined cookies, customers with ad blockers and customers who closed the tab before the thank-you page finished loading.
This guide explains the difference between browser-side and server-side Google Ads tracking on Shopify, why the gap exists, and how to run both together with deduplication so the account sees every order exactly once.
How Browser-Side Tracking Works
- The customer clicks an ad and lands on the store with a Google click ID in the URL.
- The Conversion Linker stores that ID in a first-party cookie.
- On the thank-you page, the Google Ads conversion tag fires from the browser with order value, currency and transaction ID.
- Google matches the conversion to the click using the cookie.
It is simple, fast and fully handled in Google Tag Manager. The Shopify GA4 tracking guide on this site covers the setup, including the Customer Events pixel that makes it work on modern checkout.
Where Browser-Side Loses Orders
- Ad blockers and privacy extensions stop the GTM or Google Ads script from loading at all.
- Safari Intelligent Tracking Prevention shortens cookie lifetimes, so a customer who clicks today and buys next week loses attribution.
- Consent banners block tags for visitors who decline, and without Consent Mode nothing is modelled.
- Page abandonment happens when the thank-you page is closed before scripts run, common on mobile.
- Network failures and slow connections drop requests.
None of these are configuration errors. They are limits of running measurement in the customer's browser.
How Server-Side Tracking Works
- The customer places an order.
- Shopify sends an
orders/paidwebhook to your server-side GTM container the moment payment is confirmed. - The server container turns the order into a conversion event with the stored click ID, value, currency and transaction ID.
- It sends the conversion to Google Ads from your server.
The browser is not involved after the order, so blockers, closed tabs and dropped requests do not matter. The server also sees the order's final value after discounts and currency conversion, directly from Shopify.
The Setup, Step by Step
Step 1: Capture the click ID on the storefront
In GTM on the storefront, store the gclid URL parameter in a first-party cookie for 90 days, along with the GA4 client ID. Then write both to the Shopify cart as attributes using the Cart API, for example attributes[gclid], so they travel with the order. A small Custom HTML tag that calls /cart/update.js after the cookie is set handles this.
Step 2: Set up the server container
Create a server-side GTM container and host it on Google Cloud or a managed provider, on a subdomain of your store such as sgtm.yourstore.com. Add a Client that listens on a path like /shopify-orders and verifies the Shopify webhook HMAC signature.
Step 3: Create the Shopify webhook
In the Shopify admin, Settings, then Notifications, then Webhooks, add a webhook for Order payment with JSON format pointing at your server container path. Alternatively create it through a custom app for the API version you want.
Step 4: Map the order to a conversion
In the server container, parse the webhook into event data: transaction_id from the order ID, value from the total price, currency, items from line items, gclid and client_id from the note attributes, and the customer's hashed email and phone.
Step 5: Send to Google Ads
Use the Google Ads conversion tag in the server container with the conversion action you already use in the browser. Map the transaction ID, value, currency, click ID and user-provided data. Google Ads deduplicates conversions with the same transaction ID within the same action, so the browser and server events merge into one.
Step 6: Keep the browser tag
Leave the browser tag in place. It fires first for most customers and carries the full click context. The server event arrives seconds later and is deduplicated. For the customers where the browser tag never fired, the server event is the only record and it counts.
Deduplication Rules
- Same conversion action for browser and server.
- Same transaction ID, the Shopify order ID, on both.
- For Meta, same
event_idon the browser pixel Purchase and the Conversions API Purchase. - For GA4, send the server purchase with the same transaction ID; GA4 ignores the duplicate in the same property.
If you see conversions roughly doubling after adding server-side, one of these rules is broken.
Testing
- Place a test order with the browser tag working. Confirm the browser conversion fires in GTM Preview and the webhook arrives in the server container Preview with the correct order ID, value and click ID.
- Place a second test order with an ad blocker enabled. Confirm the browser tag does not fire and the server event still arrives.
- After 24 hours, check Google Ads for exactly two conversions, not three or four.
- Over a week, compare Google Ads conversions with Shopify orders attributed to Google in the Shopify marketing report. The gap should shrink to single digits.
What Changes in the Account
More conversions recorded means lower reported cost per acquisition and, more importantly, more signals for Smart Bidding. Target ROAS and Maximise Conversion Value strategies perform better with complete data. Stores often see bidding become noticeably more stable within two to three weeks of adding server-side conversions, simply because the algorithm is no longer learning from a biased sample of customers who happen not to use Safari.
Summary
Browser-side tracking is necessary and not sufficient on Shopify. Server-side tracking from the order webhook fills the gaps that browsers create, and with order ID deduplication the two work as one system. If your Google Ads numbers and your Shopify admin disagree by more than ten percent, this is the fix.
