Repository navigation
Read an unavailable robots.txt as the standard does, not as a refusal - #32
Merged
Merged
Conversation
ADR 0013 recorded a site that would not serve /robots.txt as `blocked`, on the grounds that "a refused robots.txt counts as disallowing everything". That was written from two hosts in a two-state slice. At eighteen states it governs 482 documents, and it is stricter than RFC 9309, which puts every 400-499 in one "unavailable" class where a crawler may fetch. Only 5xx requires assuming a disallow. The cost of the rule had never been measured, so it was. All 482 documents come from seven hosts, so this is a census rather than a sample: one request each, two seconds apart, status and content type only, nothing stored. Five of the seven serve the document at 200 with application/pdf — Medica, Anthem, Wellpoint, AvMed and Mending. The prediction going in was the opposite, that a 403 on robots.txt meant a WAF refusing the client outright. That holds for UnitedHealthcare and Mercy, and they refuse on their own account: their 403 on the PDF still records the document as blocked, and is still never worked around. Nothing else moves. HTTPS only, public hosts re-checked at every redirect, two seconds between requests to one host, the 15 MB cap, and a real Disallow rule all stand. A 5xx robots.txt now blocks where it used to fall through to allow_all, which is the safer direction. A network error still fails open, as it did, but says so in the log instead of doing it silently.
Badri-Rajendran
force-pushed
the
feature_robots_unavailable
branch
from
September 22, 2026 19:44
a32cffc to
6714cae
Compare
The eight affected states were refreshed, 1,496 documents re-checked. The estimate held: coverage went from 1,940 plans (59.2%) to 2,274 (69.4%). Two things worth saying plainly rather than reporting the headline alone. The documents this reaches parse less completely than the ones already read: `partial` went from 22 to 138, so 69.4% is 2,136 whole documents and 138 partial ones. They are honestly marked and their text is searched, which is what ADR 0017 is for, but the number is not 2,274 whole documents. UnitedHealthcare and Mercy now fail on their own status — www.uhc.com returns 403 for the document itself, so those records carry that reason instead of one inferred from a different file. Same blocked status, same sentence to the user, accurate cause. ADR 0016 verified: 1,264 PDFs before, 1,593 after, every earlier file still present, and the archive grew by exactly the 77 documents issuers had replaced.
This was referenced Sep 22, 2026
The robots.txt change moved them the moment it ran: 2,274 plans covered rather than 1,940, and 138 partly-read documents rather than 22.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ADR 0013 recorded a site that would not serve
/robots.txtasblocked:That was written from two hosts in a two-state slice. At eighteen states it governs 482 documents — the second largest reason for missing coverage after a real
Disallowrule.It is also stricter than the standard it implements. RFC 9309 §2.3.1.3 puts every 400–499 in one class, "Unavailable", and says a crawler may access the site's resources; only §2.3.1.4's "Unreachable" (5xx) requires assuming a complete disallow. A 401 or 403 on
/robots.txtis a web server declining to serve one file — not a statement about the documents.The measurement
The cost of the rule had never been measured. All 482 documents come from seven hosts, so this is a census, not a sample: one request each, two seconds apart, status and content type only, nothing stored or parsed.
/robots.txtapplication/pdfapplication/pdfapplication/pdfapplication/pdfapplication/pdfFive of seven serve the document without objection. My prediction going in was the opposite — that a 403 on
robots.txtmeant a WAF refusing the client outright, so the PDF would refuse too. That holds for exactly two hosts, and both already refuse on their own account.Implementation
_robots()now follows the status, as RFC 9309 describes:Disallow.robots.txt could not be served. Stricter than before, where any non-200 that was not 401/403 fell through toallow_all.Nothing else moves. HTTPS only, public hosts re-checked at every redirect, 2 s per-host pacing, the 15 MB cap, and the PDF check all stand. A 401 or 403 on the document itself is still
blockedand still never worked around — which is exactly what keeps UnitedHealthcare's 229 and Mercy's 6 out, on their own servers' say-so rather than on an inference from a different file.A network error still fails open, as it did, but now logs instead of doing it silently. ADR 0020 records that as a deliberate, unmeasured divergence rather than changing it blind.
Validation
uv run pytest -q— 406 passing.tests/test_sbc_fetch.pygoes from 16 to 33 tests: 4xx parametrised over 401/403/404/410 proceeds, 5xx over 500/503 blocks with the new reason, and a 403 on the PDF itself still blocks.ruff check,bandit -r src -llclean. No migration.Security / DB / API impact
Note
Stacked on #31 → #30 → #29 → #28.