toolkit

Link & image checker · Playbook

Link thresholds — the numbers Link & image checker grades on, and where they come from

Issue #70: these were hardcoded in klink.mjs and surfaced nowhere, so “why is that a warning?” had no checkable answer. This is the reader-facing side of the source field each finding carries.

Read the caveat before quoting a number. Only the broken-link rule is absolute — a 404 is a 404. Everything below it is a default tuned for a general site, not a standard. A documentation site with 4,000 legitimate internal links, a directory page whose job is to be a list of links, or a design system where “Learn more” is the deliberate house pattern all have a different right answer. The right band depends on the site.

What fails the page

Check Threshold Level
Broken link final status ≥ 400, or a network error / timeout error
Broken image as above error
Parked domain registrar parking markers in the response error

A CDN/WAF bot-challenge (Cloudflare, WP Engine GES) is not counted broken — a real browser passes it. Those are reported separately as challenged, which is why a protected site does not FAIL here. Cloudflare /cdn-cgi/ obfuscation hrefs are skipped outright: a browser never fetches them literally.

What flags for review

Check Threshold Level Why this number
Generic anchor text 10% of links with text warn A tenth of a page’s links saying “click here” is a pattern, not an accident. Below that it is usually one stray CTA
Excessive links > 3,000 on one page warn Google’s long-standing crawl-budget guidance; well above any editorial page
High link count > 1,500 info Advisory only — may dilute link equity
Image-only link, no alt any warn A screen reader announces nothing; there is no threshold to tune
Malformed link any warn javascript:, empty href, bad mailto:/tel:, unencoded spaces
Insecure external link any http:// external warn Increasingly blocked or auto-upgraded
target="_blank" without rel="noopener" any warn The linked page gets a handle on window.opener
Nofollow on an internal link any warn Almost always unintentional; wastes internal link equity
Mixed content any insecure subresource on an https page warn Active (script/iframe) is graded above passive (image/media)

The generic-anchor list

^click here$ · ^here$ · ^read more$ · ^learn more$ · ^more$ · ^link$ · ^this$ · ^go$ · ^see more$ · ^view more$ · ^details$ · ^info$ · ^more info$ · ^more details$ · ^continue$ · ^continue reading$

Anchored to the whole trimmed anchor text, deliberately: “Read more about dental implants” is a perfectly good anchor and is not matched. Only a bare “Read more” is.

Run limits (not verdicts)

Setting Default Flag
Links/images fetched per page 250 each --max
Concurrent fetches 6 --concurrency
Per-request timeout 10s

The per-host politeness limiter (safe-fetch.mjs) spaces requests to one host regardless of concurrency. These defaults exist because an orchestrated run once read a throttled staging host’s back-pressure as 32 broken links; standalone the same page reported 3.

When the cap bites, klink says so — “checked first 250 of N links (raise with --max)”. A silent cap would read as “those were all of them”.