Scheduled broken-link and redirect checks that tell you the moment a link breaks — linkbruno.com
You open the console on your own site and see a column of red:
GET https://example.com/static/ads/hero-banner.webp net::ERR_BLOCKED_BY_CLIENT
GET https://example.com/js/analytics-consent.js net::ERR_BLOCKED_BY_CLIENT
Nothing is wrong with your server. The request never left the browser. ERR_BLOCKED_BY_CLIENT means something running inside the browser, almost always an extension, cancelled it. For your visitors who use ad blockers (a large share on developer-focused and tech sites), that image, script or link target simply doesn't exist.
This matters most when the blocked thing isn't an ad at all. Here's how to find out why it's blocked and how to fix it without asking anyone to turn off their blocker.
Step 1: confirm it's an extension
Open the same page in an Incognito or private window with extensions disabled (Chrome disables them in Incognito unless you've allowed them). If the errors disappear, an extension is the cause.
To find which extension, re-enable them one at a time, or check the obvious candidates first: uBlock Origin, AdBlock Plus, AdGuard, Ghostery, Privacy Badger, and the blockers built into Brave and Opera. Brave's shields block requests without any extension installed, and show up the same way.
Step 2: find the rule that matched
Content blockers use filter lists such as EasyList, EasyPrivacy and Fanboy's lists. Rules match on URL patterns, so the file name or path is usually what triggered the block, not the content.
In uBlock Origin, open the dashboard's logger (the "list" icon in the popup, or the extension's logger page). Reload your page and find the red entries. Each one shows the rule that matched and the list it came from, for example:
||example.com^*/ads/ EasyList
/analytics- EasyPrivacy
AdGuard and AdBlock Plus have similar request logs. Once you know the rule, you know what to rename.
Step 3: the usual innocent victims
These are the patterns that most often catch legitimate files:
-
/ads/,/ad/,-ad-,_ad_,adserver,bannerin paths or file names. A folder calledstatic/ads/holding product images for your "advertise with us" page gets blocked wholesale. -
analytics,tracking,track,pixel,beacon,collect,stats. Including your own first-party code with those words in it:analytics-consent.js,track-order.js(an order tracking page!),stats-chart.js. -
share,social,facebook,twitter,linkedinin file names, caught by social-widget and annoyance lists. -
popup,modal-promo,newsletter-overlay, caught by annoyance lists. - Common third-party hosts: tag managers, analytics endpoints, ad networks, many chat and feedback widgets, and some font and CDN hosts on stricter lists.
The track-order case is a good example of why this is a broken-link problem as much as a script problem. If a page's important link or button is rendered by a blocked script, that link doesn't exist for a large slice of visitors.
Tired of hunting blocked assets one URL at a time? linkbruno.com crawls the site and flags the images, scripts and links that fail to load.
Step 4: fix it
Rename first-party files and folders
For your own assets, the fix is boring and effective: rename them so they don't match ad or tracker patterns. static/ads/ becomes static/promo-images/ (check that one too; "promo" appears in some lists), track-order.js becomes order-status.js, stats-chart.js becomes dashboard-chart.js.
Then search your codebase and build output for the old names, so nothing still references them. And keep redirects from the old URLs if any of them were ever linked from outside.
Don't build core features on blocked third parties
If your checkout, sign-up form or main navigation depends on a third-party script that's on a common block list, some users can't complete those flows. Load essential functionality from your own domain, and treat third-party widgets as optional enhancements: the page should work, and the links should be visible, if they fail to load.
Make blocked loads fail softly
When you do load optional third-party scripts, handle the failure:
const s = document.createElement("script");
s.src = "https://widget.example-chat.com/loader.js";
s.async = true;
s.onerror = () => {
// Blocked or unreachable: show a plain contact link instead
document.querySelector("#chat-fallback").hidden = false;
};
document.head.appendChild(s);
A plain <a href="/contact"> fallback means the visitor still has somewhere to go.
Don't try to evade blockers for tracking
Renaming your own product images and order-status script is fixing a false positive. Disguising tracking scripts so blockers miss them is something else, it's against your visitors' explicit choice, and filter list maintainers usually catch up anyway.
Find blocked files across the whole site
Checking pages one at a time in a browser is slow. If you have a list of your own asset URLs (from your build manifest, or a crawl), you can test them against a downloaded copy of EasyList and EasyPrivacy with a filter-matching library, such as the adblock Python bindings for Brave's Rust engine or the @cliqz/adblocker package in Node. Load the lists, run every URL through the matcher with the right request type (image, script, stylesheet), and print the ones that match a blocking rule along with the rule. It takes a few minutes to set up and turns "some users say the chart is missing" into a precise list of files to rename before the next release.
Step 5: test with a blocker on
Add a blocker to your regular testing. A browser profile with uBlock Origin and default lists, used for a quick click-through before each release, catches most of these issues. For automated tests, you can load an unpacked extension in Playwright using a persistent Chromium context, which lets you run your link and smoke tests with the blocker active.
Link checkers that request URLs directly, including linkbruno.com, see these files as healthy, because the server returns them fine. The blocker-on browser test is the check that sees what those visitors see.
Related reading
- How to find and fix broken images on a website
- '0 broken links found' is a myth: 7 places link checkers don't look
- How to find mixed content (http:// links) on an HTTPS site
- Moving docs from a GitHub wiki to a docs site? An 11-point checklist so the links survive
What's the most innocent file you've had blocked? I've seen a recipe site lose its whole "Add to shopping list" feature because the script was called list-tracker.js.
Top comments (0)