How do I gracefully handle (or measure the impact of) ERR_BLOCKED_BY_CLIENT on my tracking requests?
Short answer
ERR_BLOCKED_BY_CLIENT is a browser network-stack error that fires when an extension (almost always an ad or privacy blocker) intercepts a request before it ever leaves the page, so it carries no HTTP status to inspect - it has to be caught at the client with a generic .catch()/onerror handler, not diagnosed from a response. Treat it as a distinct failure category from real network errors so you can count how much tracking data is actually being lost, rather than just silencing the console noise.
Why this happens
A normal failed request still completes the browser's network stack - it goes out, something goes wrong (timeout, 500, CORS), and you get a response object or a well-defined error to inspect. ERR_BLOCKED_BY_CLIENT is different: a browser extension's content-blocking layer intercepts the request before the network stack even opens a connection, so there is no response, no status code, and no server-side log entry for it either.
This is why it's easy to miss in monitoring - from the server's perspective, the request never happened, so server-side analytics undercount silently with no error to alert on. The only place the block is visible at all is the browser's own DevTools Network panel and the client-side JavaScript exception (or unresolved promise) at the call site.
Fix it
- 1
Wrap every tracking call so a block can't throw an uncaught error
Any fetch/XHR/beacon call to an analytics or pixel endpoint should have an explicit failure handler, since an uncaught ERR_BLOCKED_BY_CLIENT can otherwise surface as a console error or, in stricter setups, an unhandled rejection.
fetch(collectorUrl, { method: "POST", body: payload, keepalive: true }) .catch(() => { // Generic failure - could be ERR_BLOCKED_BY_CLIENT, a real network // error, or a CORS issue. There is no response object to branch on. recordBlockedOrFailedRequest(); }); - 2
Count blocks separately from successes, don't just suppress them
Increment a lightweight counter (or fire a single low-cost signal to a first-party endpoint you control) whenever the catch handler runs, so you have an actual number for how much tracking volume is being lost rather than an assumption.
- 3
Consider navigator.sendBeacon as a carrier, not a bypass
sendBeacon has different retry and lifecycle semantics than fetch/XHR (it's designed to survive page unload), which can reduce a specific class of lost events on navigation - but it is not exempt from the same URL-pattern blocking rules, so it will still be blocked if the endpoint or path matches a filter-list pattern.
- 4
If block rate matters enough to act on, look at first-party request routing
Moving the collection endpoint to a first-party subdomain, or adopting a server-side tagging pattern, reduces (does not eliminate) the block rate because fewer filter-list rules match on domain alone - this is a bigger architecture change than detection, so scope it separately once you know the actual block rate is worth the effort.
- 5
Keep the console clean without hiding the underlying signal
A caught rejection won't print an uncaught-error stack trace, but make sure your own logging (if any) doesn't spam the console on every blocked request either - log the aggregate count, not every individual occurrence.
How to verify it worked
Open DevTools > Network with an ad blocker enabled, filter to your tracking endpoint, and confirm the request shows a blocked status rather than a normal response - this is the direct evidence the error is actually ERR_BLOCKED_BY_CLIENT and not something else.
Set a breakpoint (or a temporary console.log) inside your catch handler and confirm it fires when the blocker is active, and does not fire when it's disabled on the same page and network conditions.
Confirm your blocked-request counter increments only while the blocker is active across a few repeated test loads, and stays at zero in a clean browser profile with no extensions.
Related
Still stuck, or want this checked against your specific setup?
We scope every engagement in discovery, before implementation - no assumptions about your stack.