How to Validate an llms.txt File: Spec, Links and Delivery
Validation is where most llms.txt projects quietly go wrong. A file can be pasted into a validator, come back clean, and still never be read — because the text of the file and the way the file is served are two different problems. Here is the three-layer check worth running on every llms.txt you publish.
What "Valid" Actually Means
The specification
is deliberately short: one H1 with the site name, a blockquote summary, optional free-form
details, then H2-delimited lists of links in name: description form. Of those, only
the H1 is genuinely required. That minimalism is why three different layers of checking exist,
and why a file that passes one can fail another.
| Layer | Question it answers | How to test it |
|---|---|---|
| 1. Structure | Does the file follow the format? | Parser / validator, or a markdown lint pass |
| 2. Link integrity | Does every listed URL resolve to the page you meant? | HTTP status check on each link |
| 3. Delivery | Can a crawler actually fetch it as text? | Headers, content type, cache and WAF behaviour |
Layer 1: Structure — the Checks a Parser Can Make
These are mechanical and objective. A file passes or it does not:
- Exactly one H1, on the first line. Multiple H1s, or a heading that arrives after the summary, confuse the parse.
- A blockquote summary. Optional in the spec, but the single highest-value line in the file — it is the context an assistant reads before deciding whether to keep going.
- Links as list items, with display text and an absolute URL.
- [Docs](https://example.com/docs): API referenceis valid; a bare path or a bullet with no link is a formatting error. - No prose in link sections. Paragraphs between the H2 sections are where parsers report unrecognised lines — put narrative text above the first H2.
Optionalused for the right thing. That H2 is reserved for resources that can be skipped when a consumer is short on context. Filling it with your best pages defeats the point.
# Acme Docs
> Acme is the developer documentation site for the Acme API,
> with REST and GraphQL references, SDKs and migration guides.
## Important links
- [Home](https://acme.com/): Documentation home
- [Quick start](https://acme.com/getting-started): 5-minute setup
- [API reference](https://acme.com/api): Complete REST reference
## Optional
- [Changelog](https://acme.com/changelog): Release history If you would rather not eyeball it, the llms.txt validator runs this layer and more: it flags a missing H1, an H1 that is not on the first line, multiple H1s, links with no display text, relative URLs, unrecognised lines and a file with no links at all, then returns a 0–100 AI-readiness score so you can see progress rather than a binary verdict.
Layer 2: Link Integrity — the Check Nobody Runs
Structure checks never touch your URLs. A file can be spec-perfect and full of 404s, redirect chains and pages that moved six months ago — see llms.txt maintenance for why that decay is the normal state of a published file. The audit is a loop:
# 1. pull the file
curl -s https://example.com/llms.txt -o /tmp/llms.txt -w '%{http_code} %{content_type}\n'
# 2. status of every absolute URL in it
grep -oE 'https?://[^)]+' /tmp/llms.txt | sort -u | while read -r u; do
printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -L "$u")" "$u"
done
# 3. anything still redirecting or gone
grep -oE 'https?://[^)]+' /tmp/llms.txt | sort -u | while read -r u; do
curl -sI -o /dev/null -w '%{http_code} %{url_effective}\n' "$u"
done - A 404 means the entry is worse than nothing: the consumer spent a fetch and learned nothing.
- A 301 or 302 where the effective URL differs from the one you listed means you are sending agents through a detour. List the final URL.
- Duplicates — the same page under
wwwand non-www, or with tracking parameters — split your authority across entries that look like separate resources. - A gated or login-walled link is a dead end. If the page needs a session, describe it in the file but link to the public overview instead.
Layer 3: Delivery — Where Most Failures Hide
The last layer is the one a text validator cannot see, because it never leaves the file. Four checks catch almost everything:
- Path. The file must be reachable at
/llms.txton the host that serves your pages. A copy on acdn.subdomain or a docs subdomain only covers the URLs under that subdomain. - Content type.
text/plainortext/markdown. A catch-all rewrite that answerstext/htmlis the classic failure, and it usually means an SPA fallback swallowed the path. - Status. 200, directly — not a 302 to the homepage. Redirects to marketing pages are counted as missing files by anything doing a strict fetch.
- Content. Confirm the bytes you published are the bytes being served. Edge caches happily serve the previous version of a file that changed an hour ago.
Each of those has a specific curl test and a specific fix — the delivery-level troubleshooting guide walks through all seven variants, from WAF rules to case-sensitive paths.
Make Validation Part of the Build, Not a Launch Task
A file validated once at launch is a file that is correct for one day. Put the cheapest layer in CI so a rename or a deleted page fails the build instead of silently rotting in production:
- name: Validate llms.txt
run: |
test -f public/llms.txt || exit 1
head -1 public/llms.txt | grep -qE '^# ' || exit 1
node scripts/check-llms-links.mjs public/llms.txt Keep the link check non-blocking at first while you clean up the existing entries, then make it blocking once the file is green. It is the same rhythm described in the six llms.txt mistakes that get a file ignored — only this time it is enforced by tooling rather than memory.
What a Validator Cannot Tell You
Two things, and both matter more than the mechanical checks:
- Whether your summary is any good. A grammatically valid blockquote can still be a slogan. Rewrite it as a factual description of what the site covers and who it is for.
- Whether you listed the right pages. A perfectly formatted file of your ten least important URLs passes every check and helps nobody. That judgement is editorial, and it is what separates a trusted file from a compliant one.
Use the generator to get a structurally valid first draft from your sitemap, then cut it down by hand before publishing. Validation is the last step of that workflow, not the first.