Failed to load resource: net::ERR_BLOCKED_BY_CLIENT means the request never left the visitor’s browser. Something installed in the browser, almost always an ad or tracker blocker, matched the URL against a filter list and cancelled it. Your server was not contacted, so there is nothing in your logs, and no server-side change will fix it. What you can do is find out which of your URLs match the lists, decide whether the blocked thing matters, and make the page degrade gracefully when it is missing.
The top answers for this error are short and correct: it is an ad blocker, try incognito. That solves it for the developer looking at their own console. It does not help the developer whose users are seeing a broken page because the checkout script, the analytics beacon, or the image directory was named something a filter list dislikes. This post is for that second developer: how filter lists decide, which names trip them, how to detect a blocked request in code, and what to rename.
Table of contents
- What the error actually says
- How filter lists decide
- Is it your code or their browser
- Detecting a blocked request in code
- What to rename, and what not to
- Why it only affects some users
- How this fits the rest of the stack
- FAQ
What the error actually says
Chrome reports network failures with a net:: code. ERR_BLOCKED_BY_CLIENT is the one for a request cancelled by an extension using the webRequest or declarativeNetRequest API. The client is the browser; the blocking is local. Compare it with ERR_BLOCKED_BY_RESPONSE, which is the server refusing via headers, and ERR_CONNECTION_REFUSED, where the request went out and nothing answered.
Because the request was cancelled before the network, three things are true at once. There is no entry in your access log. There is no CORS or status code involved. And it will reproduce for anyone with the same extension and not for anyone without it, which is why it feels random from the support-ticket side.
Firefox shows the same event as a request blocked by an extension in the network tab, and Safari content blockers show it as a cancelled request. The mechanism is identical: a URL pattern matched a list.
How filter lists decide
Blockers ship community-maintained filter lists. The rules in them are URL patterns with options, and they are more aggressive than most developers expect. The patterns that catch legitimate first-party resources tend to be generic path fragments:
- Path segments and file names containing ad, ads, advert, banner, sponsor, promo.
- Anything with track, tracking, tracker, analytics, pixel, beacon, telemetry, stats, metrics.
- Common analytics endpoints and script names, including first-party proxies of them, which lists learned to recognise by path.
- Social widgets and share buttons loaded from the social networks’ domains.
- Image directories named ads, banners, or promo, which are often nothing of the sort.
So /assets/images/banners/hero.jpg can be blocked on a site that has never shown an advert. So can /api/track-order, which is a shipping page. And /js/analytics.js is blocked almost everywhere, even when it is your own script that counts nothing but page views.
Is it your code or their browser
Their browser, in the sense that no server change fixes it. Your code, in the sense that your URL matched the pattern and a different URL would not. The test is simple: open the page in a private window with extensions disabled, or in a browser profile with none. If the error disappears, you have confirmed the cause. Then open the blocker’s log, which most of them expose from the toolbar icon, and it will show the exact rule that matched.
Do that before renaming anything. Some matches are on the domain, not the path, and a first-party rename will not help if the blocked thing is a third-party script. In that case the choice is between not loading it and accepting it will be blocked for a share of visitors.
Detecting a blocked request in code
You cannot read the net:: code from JavaScript, but you can observe the consequence: the script did not load, the fetch rejected, the global was never defined. Handle it explicitly instead of letting the page half-render.
// A script tag that a blocker cancels fires onerror.
const s = document.createElement('script');
s.src = '/js/metrics.js';
s.onerror = () => { window.__metricsBlocked = true; };
document.head.appendChild(s);
// A fetch that is cancelled rejects with a TypeError before any response.
try {
await fetch('/api/track-order?id=123');
} catch (err) {
// No status, no response: the request never left the browser.
showOrderStatusFallback();
}
The important design rule is that the page must work when the blocked thing is missing. Analytics that are missing should be missing silently. A checkout that depends on a blocked script should not depend on it. If a feature genuinely needs a script that lists block, load it under a name that does not match and serve it from your own domain, which is the next section.
What to rename, and what not to
Renaming is legitimate when the resource is yours and does not do what the list assumes. Renaming to sneak a tracker past a visitor who chose to block trackers is a different thing, and blockers update their lists against exactly that, so it also does not last.
- Move images out of directories called ads, banners, promo. Call them hero, media, gallery.
- Rename first-party scripts and endpoints that contain track, analytics, stats, pixel. An order tracker is /api/orders/status, not /api/track.
- Serve your own scripts from your own domain under a plain name, and set proper cache headers on them so the rename does not cost you a cold cache. On a static site the headers configuration is where those live.
- Leave third-party analytics as they are, and make sure nothing on the page breaks when they do not load.
After renaming, purge the CDN and hard-refresh, then test in a browser with the two or three most common blockers installed. A site that survives an aggressive blocker survives everything else.
Why it only affects some users
Because only some users have the extension, and different extensions ship different lists. The share of visitors with a blocker varies from a few percent on a consumer site to a majority on a developer-facing one, which is why this error shows up disproportionately on documentation sites and dashboards. Enterprise browsers add a second layer: corporate proxies and security suites block by category, and those show as the same error.
The practical consequence is that you should never rely on client-side calls for anything that must be recorded. If an event matters, the server that handled the request should record it, in its own logs. A platform that keeps runtime logs per deploy gives you the server-side count for free, and it is the only count a blocker cannot touch.
How this fits the rest of the stack
Most of the time this error is noise in your own console. When it is not, the fix is a rename and a fallback, and the lesson is that anything important should be counted on the server. If that server does not exist yet because the site is static, the RunxBuild hosting calculator shows what a small backend service beside it costs, line by line. Name things plainly, degrade gracefully, and count on the server.
Useful related references:
- React CORS Errors on Vercel: The Browser Is Not the Problem
- What Is a Cache Miss, and Why Your Hit Ratio Is Lower Than You Think
- Submitting a Website to Search Engines: What Still Works in 2026
- Response headers on RunxBuild
FAQ
What causes net::ERR_BLOCKED_BY_CLIENT?
A browser extension, almost always an ad or tracker blocker, matched the request URL against a filter list and cancelled it before it left the browser. The server was never contacted. Corporate security proxies and content blockers produce the same error the same way.
Is ERR_BLOCKED_BY_CLIENT caused by my code or the user’s browser?
The block happens in the browser, so no server change fixes it. But it triggered because your URL matched a pattern: directories called ads or banners, scripts with analytics or track in the name. Confirm by loading the page with extensions disabled, then check the blocker’s log for the rule that matched.
How do I fix ERR_BLOCKED_BY_CLIENT for a specific resource?
If the resource is yours, rename it to something that does not match filter patterns and serve it from your own domain. If it is a third-party tracker, accept that a share of visitors will block it and make sure the page works without it. Never make a checkout or a core feature depend on a resource that lists commonly block.
Can I detect or catch this error in JavaScript?
Not the net:: code itself, but its effects. A cancelled script tag fires onerror; a cancelled fetch rejects with a TypeError and no response object. Handle both and render a fallback. Anything that must be recorded should be recorded server-side, where a client blocker cannot reach it.
Why does the error only happen for some users?
Only some visitors run a blocker, and different blockers use different lists. Developer-facing sites see it far more because their audience installs blockers at a higher rate. Corporate networks add category-based blocking on top, which shows up as the same error.