A working URL can still be a broken link.
A status code answers whether a server replied. It does not tell you whether the page is the one you meant to send someone to. That distinction became the starting point for a small rebuild of this site’s link-checking workflow.
The verdict: check the destination, not just the response—and say plainly when automation cannot verify it.
Before: no automated destination check
The site already had a canonical list of outbound destinations and tests for its generated CloudFront redirects. Those tests could catch configuration drift. They could not tell whether the destination was a real channel or an error page served with HTTP 200.
The new regression fixture reproduces the failure mode: a page titled “Channel Not Found” that returns HTTP 200. A status-only check accepts it. The content check rejects it.
The rebuild: one script, two kinds of verification
The checker uses Node’s built-in APIs, with no new dependencies. It reads the built site, serves it on a temporary local HTTP server, and checks internal paths and fragment anchors. It also compares generated outbound redirects with their canonical configuration.
- In preview CI: deterministic fixtures and offline checks run before upload. A missing internal target or mismatched generated redirect stops the preview deployment.
- In the full local audit: live redirects must point to the expected destination. External HTML titles and headings are inspected for soft-404s; challenges and empty app shells are flagged for browser verification.
Third-party bot walls do not belong in a deterministic deployment gate. Keeping those network checks explicit and local prevents a captcha from becoming a false deployment failure—or a false pass.
Review caught a mistake in the checker itself
The first version treated an extensionless link such as /privacy as missing, even though the preview router serves /privacy.html. Review also identified a testing gap: the fixtures proved that broken links failed, but did not require a clean site to pass.
The follow-up mirrored the router’s rules and added a clean four-page fixture. It checks extensionless routes, anchors, directory paths, assets, and redirects, then runs the actual CLI and requires exit code zero. The broken-site fixture still requires exit code one.
The receipt: what passed, and what did not get claimed
Read the published verification receipt: the recorded commands, audit totals, and review corrections for this build. The implementation shipped in PR #52, merged as 036cf9f, and its production deployment passed.
The source repository and review discussion are private. This receipt is an operator-published record of the checks, not independent certification or a public source release.
- Five link-checker tests and 17 existing CloudFront assertions passed.
- The built-site offline audit recorded 127 successful checks with zero errors.
- The full network audit recorded 153 OK results, zero errors, and six destinations requiring browser verification. All 15 live redirect hops matched configuration.
Those counts are a snapshot from this build—not a permanent promise that every link works. A title heuristic can miss a failure or flag legitimate error documentation. It does not prove a JavaScript app is usable. The six browser-required results were not counted as verified destinations.
No traffic, revenue, or time-saving claim is attached. The measured result is narrower: this preview workflow now catches the tested classes of link breakage before upload.
Try the smaller rebuild
Start with one route you care about. Require a real target and anchor to pass. Then add a deliberately broken fixture and make sure the command exits nonzero. Only after both paths work should the check become a deployment gate.
These are the commands used in this private repository, shown as the workflow record—not a downloadable checker or commands available in every project:
npm run build— produce the site to audit.npm run test:links— verify the regression fixtures.npm run check:links -- --offline— check the build without external network requests.npm run check:links— add the live checks.
The last command adds live network checks. Read the browser-required results rather than treating an exit code of zero as complete verification.