Skip to content

Retry with GET when HEAD fails, not just on 405 - #29

Merged
dvdstelt merged 1 commit into
mainfrom
fix/head-404-false-positive
Aug 14, 2026
Merged

dvdstelt merged 1 commit into
mainfrom
fix/head-404-false-positive

Conversation

@dvdstelt

Copy link
Copy Markdown
Owner

Problem

Some hosts answer HEAD with an error for a page they serve fine on GET. support.google.com is the clearest case: it returns 404 to HEAD and 200 to GET for the same URL.

HyperHawk did HEAD first and only retried with GET on a 405, so a HEAD-only 404 went straight to the broken-link path and got posted as an inline review comment:

The external link returns HTTP 404. Verify the URL is still valid or remove the link.

Every Google Analytics help link in a PR was flagged this way, even though the pages are live.

Fix

checkExternalLink now retries with GET on any 4xx/5xx from HEAD instead of only 405, and the GET result decides the verdict.

Two side effects, both wanted:

  • The existing 401/403/429 auth-wall whitelist now sees the GET status, so it only kicks in when GET is genuinely blocked too.
  • The redirect-suggestion path becomes reachable for these hosts. Previously a HEAD error discarded the redirect entirely.

Cost is bounded: only links that already look broken pay a second request, and followRedirects cancels the response body as soon as headers arrive, so the GET costs about what the HEAD did.

Verification

Against the real URLs:

URL Before After
support.google.com/analytics/answer/11109416 BROKEN 404 OK 200
support.google.com/analytics/answer/9143382 BROKEN 404 OK 200 + redirect suggestion
support.google.com/analytics/answer/00000000 (does not exist) BROKEN 404 BROKEN 404

Two new routes on the snapshot test server:

  • /head-404-get-200 - 404 on HEAD, 200 on GET, expected ok
  • /head-404-get-redirect - 404 on HEAD, 301 on GET, expected redirect suggestion

The pre-existing /not-found route 404s on both methods and is still reported broken, so genuine breakage is not masked.

npm run typecheck and npm run test:snapshot pass. dist/ is rebuilt and committed.

Docs

docs/how-it-works.md and docs/troubleshooting.md both documented the old 405-only rule and are updated.

Follow-up, not in this PR

Redirect suggestions can capture session parameters. answer/9143382 suggests a URL carrying ?visit_id=...&rd=2, which is stale the moment it is applied. That weakness predates this change, but this PR makes it reachable for HEAD-hostile hosts, so it will surface more often. Stripping known session/tracking params in buildRedirectUrl would address it.

Some hosts answer HEAD with an error for a page they serve fine on GET.
support.google.com returns 404 to HEAD but 200 to GET, so every Google
Analytics help link was reported as a broken external link on PRs.

The retry now triggers on any 4xx or 5xx from HEAD instead of only 405,
and the result of the GET decides the verdict. Genuinely missing pages
still fail both methods and are still reported. Only failing links pay
the extra request, and followRedirects discards the body once headers
arrive, so the GET costs roughly what the HEAD did.

Also covers the case where the GET redirects: the redirect suggestion
now works for these hosts instead of being lost behind the HEAD error.
@dvdstelt dvdstelt changed the title 🐛 Retry with GET when HEAD fails, not just on 405 Retry with GET when HEAD fails, not just on 405 Aug 13, 2026
@dvdstelt dvdstelt added this to the 1.12.0 milestone Aug 14, 2026
@dvdstelt
dvdstelt merged commit 64da46a into main Aug 14, 2026
3 checks passed
@dvdstelt
dvdstelt deleted the fix/head-404-false-positive branch August 14, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant