Checking external links means pointing a robot at several hundred other people’s servers every week. Doing that carelessly gets you blocked, which breaks the checker, and it is also just rude. This post is about doing it properly, and about the status codes that lie.
Key takeaways
One request per unique URL per run, with the result shared by every page linking to it.
HEAD first, GET only if HEAD is refused. Many servers dislike HEAD.
Rate limit per host, not globally. One slow host must not stall the run.
403 and 429 usually mean ’not to a robot’, not ’broken’.
A soft 404 — a 200 that says the page is gone — needs its own check.
The request
Fig 1. How one URL is checked. The cache is the difference between a few thousand requests and a few hundred thousand.
Database
App integration
Machine learning
Networking
Management
Analytics
Rate limiting per host
A global rate limit is the obvious implementation and it has the wrong shape: it either hammers a single small host that happens to be linked forty times, or it slows the whole run to the speed of the slowest server.
Per-host limits — say one request a second to any single host, with several hosts in flight — solve both. It also means one unresponsive server delays only its own links, and a timeout budget per host keeps a dead domain from consuming the run.
Status codes that lie
Code
Looks like
Usually means
403
Forbidden
The server does not serve robots. Not broken.
429
Too many requests
You went too fast. Back off and retry.
405
Method not allowed
HEAD is refused. Try GET.
999
Nonsense
One large social network’s way of saying no. Not broken.
503
Unavailable
Often temporary, sometimes a bot wall. Retry next run.
200
Fine
Sometimes a soft 404. Check the content.
The first four are the reason a naive checker produces a report full of links that are perfectly fine. A 403 from a site that blocks automated requests is not a broken link and reporting it teaches whoever reads the report that the report is wrong.
So those codes are recorded as unverifiable rather than broken. They appear in a separate short section of the report, once, with an explanation — because a person checking one by hand is a perfectly reasonable resolution and telling them twenty times is not.
Soft 404s
A page that returns 200 and says “this page no longer exists” is broken in every way that matters to a reader and invisible to a status check. They are common on sites that have been migrated, where the new platform serves a friendly page instead of a 404.
Fig 2. How a soft 404 is detected. A redirect to the site root is the most reliable of the three signals and the cheapest to check.
Machine learning
Management
Analytics
Checking for soft 404s means fetching the body rather than just a HEAD, which costs more. So it is done only for external links that have redirected, and for internal links always — where it is cheap and where a migrated site is most likely to have them.
Next: the three-run rule, which is what keeps the report short.