Ad blockers break conversion tracking by stopping the request, not by breaking your code. A filter list matches the tracking script’s filename or the ad platform’s collection endpoint, and the browser never sends it. The visitor still browses, still fills in your form, still buys — but no client-side event ever fires, so the platform never learns the conversion happened.
This is the part that catches people out: there is no error. The page renders, the checkout completes, your tag assistant says the setup is healthy. The only symptom is a number that is quietly too low, in a report you have no reason to distrust.
What does an ad blocker actually block?
Individual network requests, chosen by pattern. A blocker ships a filter list of rules, each one matching a domain, a path, or a filename. When the page asks for a matching URL, the extension cancels the request before it leaves the browser.
The rules are public, so you can read exactly what happens to your stack. EasyPrivacy — the tracking list that ships enabled by default in uBlock Origin and AdGuard — carries these entries:
||facebook.com/tr/
||facebook.com/tr?
||connect.facebook.net/signals/$third-party
||analytics.tiktok.com^
||ct.pinterest.com^
||bat.bing.com^
The first two matter most. facebook.com/tr is the Meta Pixel’s collection endpoint — the URL every browser-side event is sent to. Blocking it means the pixel can load, initialise, and fire perfectly, and still deliver nothing. Every platform in that list loses its browser-side path the same way.
The same list also blocks the script by filename, regardless of who serves it:
/fbevents.js
/fbevents.min.js
/facebook_fbevents.js
/facebook-pixel.js
/fbpixel.js
Those rules have no domain attached. They match any host, which is the detail that defeats most workarounds.
Blockers do not always cancel the request outright. uBlock Origin’s privacy list replaces some scripts with a stub instead:
||google-analytics.com/analytics.js$script,xhr,redirect=google-analytics_analytics.js:5
The redirect= option substitutes a neutered local file that defines the expected functions and does nothing with them. Your page keeps working, your console stays clean, and your analytics receives nothing.

Why does blocked tracking look like it is working?
Because every piece of feedback you have about your tracking is generated in a browser — usually yours, usually without a blocker. A blocked visitor produces no error you can see: no failed request in your logs, because the request was never made, and no exception, because nothing threw.
The three checks people trust all share that flaw. A pixel-helper extension inspects the page you are on. A test conversion is a conversion you performed. Real-time event views show the events that arrived, never the ones that did not.
Downstream, the failure arrives disguised as insight. Conversions firing with no click data land in the unattributed bucket, which is much of why ad conversions show as Direct more often than media buyers expect. Blocked visitors who convert never reach that bucket — they are absent, and absence is not a line item anywhere.
What does a blocked visitor still do on your site?
Everything except emit client-side events — which is the distinction worth internalising, because it separates the parts of your measurement that die from the parts that do not.
A visitor running uBlock Origin still clicks your ad, so the click ID still arrives in your landing page URL. Their browser still requests your pages from your server, with an IP address and user agent attached. They still type an email and a phone into your form, submit it or abandon it, and still pay — and that order lands in Shopify, WooCommerce, Stripe or your CRM with their email on it.
Every one of those facts is created on your infrastructure. None of them requires the ad platform’s script to run. That is the whole basis of first-party attribution, and it is why a blocked visitor is a measurement problem rather than a measurement dead end.
How do you measure how much of your traffic blocks tracking?
Compare a count only your server can produce against the same count from the browser, over the same window. You do not need a published industry average, and should not trust one — blocking rates vary sharply by audience, geography and device, and the only number that changes your decisions is yours. Three comparisons, cheapest first:
- Orders versus browser-side purchase events. Take the order count from your store admin for one week, and the purchase count the ad platform’s pixel recorded for the same week in your own timezone. The shortfall is your blocked-and-lost tail, plus anything the pixel dropped for other reasons.
- Server pageviews versus pixel pageviews. Your server or CDN logs every page request; your analytics logs the ones where its script ran. The ratio is your blocking rate, subject to bots inflating the server side.
- Form submissions in your database versus lead events in the ad platform. The same comparison, for lead gen.
Run all three before concluding anything. A gap in one comparison and not the others usually means a tagging bug rather than blocking. If iOS and Safari traffic carry most of the shortfall, you are looking at a browser-privacy gap as much as an extension one — auditing your iOS attribution gap separates those two causes properly.
Does self-hosting or proxying the pixel get around a blocker?
Partly, and less than the guides suggest. The three common workarounds each have a documented counter.
Serving the script from your own domain defeats domain-level rules, and runs straight into the filename rules quoted above. /fbevents.js has no domain attached; it matches your copy too. Renaming the file helps until the name is added, which is why that list of variants exists.
Routing events through a tagging server on your own subdomain is more durable, because the collection request goes to your hostname rather than facebook.com/tr. That is the real advantage of a server-side container. It is also an arms race: blocklists add rules for hosted tagging providers and the subdomain patterns they use, and a first-party endpoint that exists only to forward data to a tracker is a recognisable pattern.
Both still start in the browser. A tagging server does not help a visitor whose blocker stopped the script that would have called it. That is the ceiling on every client-side workaround: they change where the data goes, not whether anything runs.
What still works when the pixel never loads?
The server-to-server path. Instead of asking the visitor’s browser to report the conversion, your server reports it directly to the ad platform’s API — Meta’s Conversions API, and its equivalents at Google, Pinterest, TikTok and Microsoft. Meta documents the Conversions API as a way to send events directly from your server, and it is the supported route precisely because browser-side delivery is unreliable.
A browser extension cannot intercept a request between your server and Meta’s. It is not in the path, and it has no visibility into it.
What you give up is identifiers, not delivery. A server event is matched to a person using the parameters you attach: hashed email, hashed phone, the _fbp and _fbc browser cookies, IP address, user agent, and your own external ID. Meta’s documentation ranks hashed email as the highest-weight identifier. The two cookie values are collected client-side — so for a blocked visitor they are simply missing, and event match quality is scored on a thinner payload.
One of the two is recoverable. _fbc is derived from the fbclid parameter in your landing URL, which your server received directly and can reconstruct in Meta’s documented format. _fbp has no equivalent: it is a value the pixel generates in the browser, and if the pixel never ran there is nothing to recover. Anyone claiming otherwise is fabricating an identifier.
This is also why the pixel and the Conversions API are not alternatives. Keep the browser tag for the visitors who allow it — it supplies cookies your server payload cannot invent — and let the server path carry everyone else. Meta’s deduplication rules mean the overlap costs you nothing: events sharing an event_id and event name are collapsed into one.
Can ad blockers block server-side conversion tracking too?
Not the dispatch. They can still reduce what it knows.
Split the problem in two. Delivery is whether the event reaches the ad platform at all; it happens between two servers, and no browser extension participates. Matching is whether the platform can tie that event to a real person and an ad click; it depends on the identifiers in the payload and on the platform’s own ability to resolve them.
Blocking damages the second half only, and only for the identifiers that originate in the browser. A blocked buyer whose email you captured still produces a matched conversion. A blocked buyer you know nothing about produces a delivered event with weak identifiers — worth more than the nothing you had, worth less than a full payload.
What can still hurt delivery is your own capture layer. Any script served to a browser can be blocked, including the one that records form input. Public tracking lists carry no rules for PartialLeads today, but no vendor should promise that about a list anyone can add a line to. The structural answer is to not depend on it: when conversions fire from order and lead records on the server, a visitor whose script never loaded still produces a conversion event the moment their order arrives.
How does PartialLeads keep conversions flowing when the pixel is blocked?
By moving both halves of the job off the browser. The Conversions API dispatch runs from PartialLeads’ server against your purchase and lead records, so the event does not depend on the visitor’s browser cooperating at the moment of conversion.
The mechanics, specifically:
- The visitor ID cookie is set by the server, not written by JavaScript, with localStorage as backup. Safari’s restrictions on script-written cookies do not apply to it, so returning visitors stay stitched to the same person.
- Click IDs are captured from the landing request —
fbclid,gclid,gbraid,wbraid,msclkid,ttclidandepik— and stored server-side against the session. When the_fbccookie is missing or malformed, the backend reconstructs it from the URLfbclidin Meta’s documented format. The URL is treated as the source of truth. - Email and phone are captured as they are typed, before submit, then normalised — email lowercased and trimmed, phone to E.164 — and hashed per platform spec. These are the highest-weight match parameters, and they come from your form rather than from any cookie.
- Purchases arrive server-to-server from Shopify, WooCommerce, Stripe, GoHighLevel or a universal webhook, are matched to a session by visitor ID, email, phone or IP in that order of confidence, and fan out to the connected conversion APIs.
- Every dispatch carries a deterministic
event_id— a hash of the pixel, event name and record — stored in a uniqueness-constrained dedup table. A redelivered webhook or a retried send cannot double-count, and browser-side events sharing that ID collapse into one on the platform’s side.
The honest constraint is the same one that applies to every vendor: keep your client-side base tag installed. The platform cookies it sets are match parameters no server can synthesise, and PartialLeads reconstructs _fbc but never invents _fbp. With email and phone as required fields, PartialLeads consistently delivers EMQ 9+, observed across customer accounts — that condition is load-bearing, because an email-only form structurally caps the score no matter what the dispatch layer does.
Where you watch it work: the CAPI activity log shows every send with its status, platform and match quality, and the Leads list carries a per-lead API column naming which conversion APIs that lead was dispatched to. The Attribution report’s revenue-by-match-quality donut splits net revenue into partial-captured, submit-completed and unmatched — the closest thing to a direct read on what a browser-only setup would have missed.

The claim worth being precise about: every order in your store admin produces a conversion event, because the send is triggered by the order record rather than by a browser. You can verify that yourself by reconciling your admin order count against dispatched events. Whether the platform then matches each event to a user is decided by the identifiers you collected and by the platform’s own graph — that half is maximised, never guaranteed, and no tool controls it.
| What breaks | The mechanism | Where you see it in the dashboard |
|---|---|---|
| Pixel script blocked by filename rule | Server-side dispatch from lead and order records, independent of the browser | CAPI activity log, sends with no matching browser event |
| Collection endpoint blocked, pixel fires into nothing | Conversions API send from PartialLeads’ server to the platform API | CAPI activity log status column |
_fbc cookie missing for a blocked visitor |
_fbc reconstructed from the landing URL fbclid in Meta’s format |
Per-lead click ID fields, CAPI payload |
| Cookie cleared or capped, returning visitor looks new | Server-set visitor ID cookie plus localStorage backup | Journey ribbon showing multiple sessions per person |
| Conversion arrives with no identifiers | Email and phone captured before submit, normalised and hashed | Match rate KPI, revenue by match quality donut |
| Purchase never reported because the tab closed | Purchase webhook matched to the session server-side | Purchases ledger, matched wedge |
| Retries and redelivered webhooks double-count | Deterministic event_id plus uniqueness-constrained dedup table |
CAPI activity log, single row per event |
| No idea whether a specific lead was sent | Per-lead dispatch record | Leads list API column |
Tell us what's broken. We'll fix your tracking — free.
Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.
Sources
- EasyPrivacy third-party filter list (rules for
facebook.com/tr,connect.facebook.net/signals,analytics.tiktok.com,ct.pinterest.com,bat.bing.com): https://raw.githubusercontent.com/easylist/easylist/master/easyprivacy/easyprivacy_thirdparty.txt - EasyPrivacy general filter list (filename rules
/fbevents.js,/fbevents.min.js,/facebook_fbevents.js,/facebook-pixel.js,/fbpixel.js): https://raw.githubusercontent.com/easylist/easylist/master/easyprivacy/easyprivacy_general.txt - uBlock Origin privacy filter list (
redirect=surrogate substitution forgoogle-analytics.com/analytics.js): https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/privacy.txt - Meta for Developers — Conversions API overview: https://developers.facebook.com/docs/marketing-api/conversions-api
- Meta for Developers — Conversions API, customer information parameters: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
- Meta for Developers — Conversions API, fbp and fbc parameters: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc
- Meta for Developers — Deduplicate Pixel and Conversions API events: https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events
- Meta Business Help Centre — About event match quality: https://www.facebook.com/business/help/765081237991954