Technology Aug 24, 2026 · 6 min read

A Broken-Link Check Counts 404s. The Resource That Breaks Your Padlock Returns 200.

Originally published on the Merlonix blog. A broken-link checker does one well-defined thing: it walks the <a href> links on a page, fetches each target, and flags the ones that come back wrong — a 4xx, a 5xx, a timeout, a connection that never opens. That answers a real question: which links...

DE
DEV Community
by Merlonix
A Broken-Link Check Counts 404s. The Resource That Breaks Your Padlock Returns 200.

Originally published on the Merlonix blog.

A broken-link checker does one well-defined thing: it walks the <a href> links on a page, fetches each target, and flags the ones that come back wrong — a 4xx, a 5xx, a timeout, a connection that never opens. That answers a real question: which links point at something that isn't there. Run a clean scan and you get a green report and a reasonable feeling that the page is healthy.

But "every link resolves" and "this page works in a browser" are not the same claim, and the gap between them is a resource class a status-code check is structurally blind to. The resource most likely to visibly break an HTTPS page doesn't fail with a status at all. It returns 200. It's mixed content — an http:// subresource loaded by an https:// page — and because the resource genuinely exists, a checker that grades links on their HTTP status waves it straight through.

The 200 that still breaks the page

When a page served over https:// pulls in a subresource over plaintext http:// — an image, a script, a stylesheet, an iframe — that's mixed content. The subresource itself is usually fine: request it directly and it's a healthy 200. Nothing is "broken" in the sense a link checker means. The break happens in the browser, on the security boundary, and it depends on what kind of resource it is:

  • Active mixed content — a <script>, a stylesheet <link>, or an <iframe> over http:// — is blocked outright. The browser refuses to load it on a secure page. A blocked script means the JavaScript that depended on it silently doesn't run; a blocked stylesheet means the page renders unstyled. No 404 anywhere — the file was reachable, the browser just won't use it.
  • Passive mixed content — an <img>, <video>, or <audio> over http:// — modern browsers try to auto-upgrade to https://, and if that secure version doesn't exist, they block it too. Either way the padlock stops being a clean padlock: the address bar drops to "Not fully secure," and on the blocked case you get a missing image on a page whose image URL returns a perfectly good 200 over http.

So the failure is real and user-visible — a dead script, an unstyled page, a broken image, a downgraded padlock — but it is invisible to any tool that decides "broken" by fetching a URL and reading its status code. The URL isn't broken. The combination of a secure page and an insecure subresource is.

Why it shows up silently

Mixed content is rarely typed in on purpose. It leaks in on a timeline you don't control:

  • A third-party embed — an analytics snippet, a chat widget, an ad tag, an old badge — hardcodes an http:// script URL.
  • A CDN or asset host is referenced with an absolute http:// URL that survives a move to HTTPS because nothing ever re-fetched it to notice.
  • A protocol migration flips the site to HTTPS but leaves absolute http:// URLs baked into content, a theme, or a database field.
  • A Content-Security-Policy that had upgrade-insecure-requests gets relaxed to ship one stubborn widget, and every previously-upgraded subresource quietly reverts.

Every one of those ships green through a broken-link check, because every one of those URLs answers 200.

How to actually find it

Mixed content is found by looking at the protocol of the subresources a secure page loads, not the status of its links:

  1. The browser console is the ground truth: it logs Mixed Content: warnings for every upgraded or blocked resource, and the padlock's site-information panel tells you the page isn't fully secure. This catches resources injected by client-side JavaScript, which a static scan of the served HTML can't see.
  2. CSP turns it into a signal you don't have to eyeball: Content-Security-Policy-Report-Only: block-all-mixed-content (or the reporting directives) will report every insecure subresource without breaking anything, so you can find them before you enforce.
  3. A checker that inspects subresource protocols — not just link statuses — catches the ones present in the served HTML in a single pass.

The free broken-links checker does both halves in one scan: it walks the page's <a href> links and reports the ones that return an error status or time out or fail to connect — and, separately, it reads the page's <img>, <script>, stylesheet, and <iframe> subresources and flags every one loaded over http:// on an https:// page as mixed content, with the exact insecure URLs. It reads the HTML the server sends, so resources injected later by client-side JavaScript still need the browser console — but the hardcoded http:// embeds that cause most real mixed-content breakage are exactly the ones in that served HTML. No signup, one page at a time.

What a link check is good for — and what to check past it

A broken-link check isn't the wrong tool; it's a narrow one. Dead outbound links, a deleted product page still linked from your nav, a 5xx on a resource you own — all real, all worth catching, all what a status check is for. Just don't read "every link resolves" as "this page is fine over HTTPS." Once the links are clean, the questions a status code can't answer are:

  • Does any http:// subresource load on an https:// page? (Mixed content — the padlock and the blocked-script break.)
  • Does the page's sitemap point at URLs that are actually reachable? (A sitemap listing dead <loc> entries misroutes crawlers, and it's another thing a spot-check of visible links skips.)

The one-line version: a broken-link check grades URLs by their status code, and mixed content is the failure that keeps a perfect status code while breaking the page — a 200 resource the browser blocks, on a secure page whose padlock quietly downgrades. Check the links, then check whether any of them are insecure subresources on a secure page.

And it comes back. A redeploy that re-adds an old widget, a CDN swap, a CSP relaxed to ship one integration — each reintroduces an http:// subresource that answers 200 and passes the next link check exactly like it passed the last one. Merlonix crawls your site on a schedule, finds broken links and mixed content across every page, and alerts you the moment a new one appears — the same way it watches SSL and DNS: continuously, from outside your stack, so a padlock that silently went from clean to "not fully secure" is something you hear from monitoring, not from a customer. Run the free broken-links scan to see where a page stands right now, and browse the rest of the free tools while you're there.

DE
Source

This article was originally published by DEV Community and written by Merlonix.

Read original article on DEV Community
Back to Discover

Reading List