The client
A Quebec yarn and knitting retailer, with shops and a Shopify online store in French and English. We’ve run its Meta ads (Facebook and Instagram) since spring 2025. The ad budget is real, so the quality of the data Meta gets matters too: Meta uses that data to decide who sees the ads.
The challenge
Before the project, data went to Meta through Shopify’s built-in Facebook and Instagram channel. It worked, but:
- No control or visibility over what was sent to Meta, or how duplicates were handled.
- Ad blockers and browsers increasingly limit Meta’s cookies and scripts, so tracking loses sales.
- Meta’s automatic events added noise (button clicks logged as events).
- Quebec Law 25: Meta is ad profiling, so nothing should be sent without the customer’s consent.
- A Google Analytics setup run by another consultant that we had to leave untouched.
What we did
- Audit of the existing tracking: every pixel, every Google Tag Manager container, the cookie banner, and how Meta received purchases.
- A tracking server in Canada with Stape (Canada East region), on a subdomain of the store. Data goes through the client’s own domain before reaching Meta, and stays on a server in Canada, which holds up well under Law 25.
- Two Google Tag Manager containers just for Meta, one in the browser and one on the server, kept separate from the existing Google Analytics setup so nothing broke.
- Seven events from both sides: page views, product views, searches, add to cart, checkout started, payment info and purchases. Each event leaves the browser and the server with the same ID, so Meta counts it once.
- Consent first: the checkout pixel only loads if the shopper accepts marketing cookies.
- Meta’s automatic events turned off, so only events we control are sent.
- Real-world tests: one French and one English order, checked in Meta and in the server logs, then cancelled.
- Go-live in under an hour: we turned off the Shopify channel’s data sharing as the new tracking went live, so Meta never got each purchase twice.
What got in the way (and how we fixed it)
Add-to-carts counted twice
The theme sent the same add-to-cart twice on every click. We added a filter that only lets the first of two identical sends a few seconds apart through. Remaining pairs went from about 100% of clicks to about 8%, mostly genuine quick adds.
Shopify checkout is invisible in preview mode
Shopify runs checkout tracking in an isolated frame, with no cookies. Google Tag Manager’s preview tools can’t attach to it. We rebuilt that environment to confirm the events still went out, then checked checkout in production through the server logs and Meta.
A setting that pointed to the wrong site
The tracking server address pointed to the main site instead of the tracking subdomain. Fixed on test day.
Results
| Result | |
|---|---|
| Server events rejected by Meta | 0 |
| Purchases sent by the server | 68 unique orders |
| Purchases with the customer’s email | 100% |
| Online orders covered | About 7 in 10 (the rest: mostly free products and shoppers who decline marketing cookies) |
| Add-to-carts counted twice | From about 100% to about 8% |
What we learned
Fewer sales sent, but the right ones
Tracking that respects consent doesn’t send everything to Meta. That’s on purpose. What matters is that every purchase sent is complete, not duplicated, and tied to the right customer.
Keeping Meta separate protects everything else
Putting Meta in its own containers let us install everything without touching another consultant’s Google Analytics setup. And rolling back takes three clicks.
Judge Meta on Meta and on real sales
Google Analytics always undercounts sales from Meta. We follow purchases in Ads Manager and the ratio of total online sales to ad spend.