When a user buys something on your site, here's what actually happens:
Meta fires a Purchase event. Google fires a Purchase event. TikTok fires a Purchase event. All three claim 100% credit. Your dashboard says you had 3x the revenue you actually made. You make budget decisions based on this. You're wrong.
## The Mechanism
Standard ad pixels work like this:
```
User buys → browser fires gtag/fbq → outbound HTTP request → ad platform server
```
The problem is step 3. Ad blockers intercept at the network level. iOS ITP (Intelligent Tracking Prevention) purges client-side cookies after 7 days. Firefox Enhanced Tracking Protection blocks third-party requests entirely.
The result: your Meta pixel might be seeing 38% fewer purchase events than actually occurred. GA4 sees 16% fewer. You don't know, because you have no ground truth to compare against.
## The Harder Problem: Multi-Touch Attribution
Even if you recover 100% of signals, you still have the attribution problem. Meta, Google, and TikTok each run last-click (or self-serving multi-touch) models that claim 100% credit for every conversion they touched.
The reality: a user might have:
1. Clicked a TikTok ad 10 days later (Safari purges cookie at day 7 — that click is now untracked)
2. Bought via a Klaviyo email on day 21
Standard analytics attributes this to Klaviyo. Meta claims it because they got a view. TikTok's pixel is broken after the ITP purge. ChatGPT gets $0. Nobody knows what actually drove the sale.
## The Pre-Block Capture Pattern
One approach that actually works at the browser level: intercept pixel calls before they hit the network.
Most pixels attach to window globals. You can wrap these functions:
```javascript
const originalFbq = window.fbq;
window.fbq = function(...args) {
// Capture payload locally first
captureToFirstPartyServer(args);
// Then let the original call proceed (might get blocked, doesn't matter)
return originalFbq.apply(this, args);
};
```
By wrapping at the function level, you capture the raw payload the exact millisecond it's created — before any ad blocker can intercept the outbound request. You then forward it via your own first-party domain, which ad blockers can't block without breaking your site entirely.
This is the core of what Zyro (https://zyro.world) calls their "Omni-Interceptor" — but the pattern itself is straightforward to implement if you want to roll your own.
The wrapping loop should poll because some pixels initialize asynchronously:
```javascript
const huntingLoop = setInterval(() => {
if (window.fbq) {
wrapPixel('fbq', window.fbq);
clearInterval(huntingLoop);
}
}, 500); // Poll every 500ms, max 5 seconds
```
## Fractional Attribution Without Losing Your Mind
Once you have a reliable server-side event stream, you need a model that doesn't just credit the last touch.
For each session, record:
- Acquisition source (UTM params, referrer, fbclid, gclid, etc.)
- Behavioral engagement signals (time on page, scroll depth, adds to cart)
- Timestamp
On conversion, apply fractional credit weighted by recency and engagement. A touch 1 day before conversion at 80% scroll depth gets more credit than a view 30 days ago.
## 7-Day Benchmark Data
Running platform-reported vs. server-side verified conversions:
Meta fires a Purchase event. Google fires a Purchase event. TikTok fires a Purchase event. All three claim 100% credit. Your dashboard says you had 3x the revenue you actually made. You make budget decisions based on this. You're wrong.
## The Mechanism
Standard ad pixels work like this:
``` User buys → browser fires gtag/fbq → outbound HTTP request → ad platform server ```
The problem is step 3. Ad blockers intercept at the network level. iOS ITP (Intelligent Tracking Prevention) purges client-side cookies after 7 days. Firefox Enhanced Tracking Protection blocks third-party requests entirely.
The result: your Meta pixel might be seeing 38% fewer purchase events than actually occurred. GA4 sees 16% fewer. You don't know, because you have no ground truth to compare against.
## The Harder Problem: Multi-Touch Attribution
Even if you recover 100% of signals, you still have the attribution problem. Meta, Google, and TikTok each run last-click (or self-serving multi-touch) models that claim 100% credit for every conversion they touched.
The reality: a user might have: 1. Clicked a TikTok ad 10 days later (Safari purges cookie at day 7 — that click is now untracked) 2. Bought via a Klaviyo email on day 21
Standard analytics attributes this to Klaviyo. Meta claims it because they got a view. TikTok's pixel is broken after the ITP purge. ChatGPT gets $0. Nobody knows what actually drove the sale.
## The Pre-Block Capture Pattern
One approach that actually works at the browser level: intercept pixel calls before they hit the network.
Most pixels attach to window globals. You can wrap these functions:
```javascript const originalFbq = window.fbq; window.fbq = function(...args) { // Capture payload locally first captureToFirstPartyServer(args); // Then let the original call proceed (might get blocked, doesn't matter) return originalFbq.apply(this, args); }; ```
By wrapping at the function level, you capture the raw payload the exact millisecond it's created — before any ad blocker can intercept the outbound request. You then forward it via your own first-party domain, which ad blockers can't block without breaking your site entirely.
This is the core of what Zyro (https://zyro.world) calls their "Omni-Interceptor" — but the pattern itself is straightforward to implement if you want to roll your own.
The wrapping loop should poll because some pixels initialize asynchronously:
```javascript const huntingLoop = setInterval(() => { if (window.fbq) { wrapPixel('fbq', window.fbq); clearInterval(huntingLoop); } }, 500); // Poll every 500ms, max 5 seconds ```
## Fractional Attribution Without Losing Your Mind
Once you have a reliable server-side event stream, you need a model that doesn't just credit the last touch.
For each session, record: - Acquisition source (UTM params, referrer, fbclid, gclid, etc.) - Behavioral engagement signals (time on page, scroll depth, adds to cart) - Timestamp
On conversion, apply fractional credit weighted by recency and engagement. A touch 1 day before conversion at 80% scroll depth gets more credit than a view 30 days ago.
## 7-Day Benchmark Data
Running platform-reported vs. server-side verified conversions:
| Source | Platform Reports | Server Truth | Delta | |--------|-----------------|--------------|-------| | GA4 | 202 orders | 241 orders | -16% | | Meta | 149 orders | 241 orders | -38% |
Meta's pixel is missing 38% of purchase events. You're making budget allocation decisions on data that misses a third of your conversions.
---
Discussion: Has anyone built their own pre-block capture layer? Curious what approaches others have taken for first-party attribution.
Further reading: Zyro (https://zyro.world) — the tool we built to solve this at scale, if you'd rather not DIY.