Fixing Broken Links in llms.txt
Every link in llms.txt is a promise that the page behind it is worth reading. When an entry returns a 404, bounces through redirects, lands on a page marked noindex or is refused to crawlers, a tool that trusts the file wastes a request or reads the wrong thing, and the file starts to look neglected.
The llms.txt Auditor tests each linked URL and reports the ones that fail. This guide explains what each kind of failure means, how to reproduce it yourself, what to change for each one, and how to catch new breakages automatically.
What counts as a broken link here
A link can be "broken" for a reader in more ways than a 404. For llms.txt, the standard is stricter than for an ordinary web page, because the file is meant to be a hand-picked list of the best URLs on the site. Anything other than a direct, public, indexable page that answers with a 200 is worth fixing.
| Finding | What the reader gets | Usual cause |
|---|---|---|
| 404 or 410 | An error page | Page deleted, slug changed, typo in the file |
| 5xx | A server error, sometimes only intermittently | Faulty template, timeout, overloaded host |
| Single redirect | The right page, after an extra request | http rather than https, www mismatch, missing trailing slash, page moved |
| Redirect chain or loop | Several hops, or no page at all | Old redirects stacked on top of each other |
| Soft 404 | A 200 response showing "page not found" or the home page | CMS redirecting missing pages to the home page, or catch-all routes in a JavaScript app |
| Noindex | A page the site has asked search engines not to index | Thin or private page listed by mistake, staging settings left on |
| Non-canonical URL | A duplicate whose canonical tag points elsewhere | Tracking parameters, print versions, filtered URLs, uppercase paths |
| 401 or 403 | A login prompt or refusal | Members-only area, or a firewall refusing bots |
| Malformed link | Nothing: the tool cannot read the URL | Relative path, missing bracket, space in the address |
The first two are genuine dead ends. The rest are softer, but all of them either waste a reader's effort or point it at something you did not intend to recommend.
Why redirects are worth fixing
Most tools follow redirects, so a redirected link usually still reaches the page. It is still worth updating, for three practical reasons:
- It costs a request. A tool reading a file of forty links with a redirect on each makes eighty requests instead of forty.
- Not every client follows every redirect. Some fetchers refuse to follow a redirect to a different host, or stop after a small number of hops, to avoid being led somewhere unexpected.
- It signals that the file is out of date. A list of addresses that all redirect tells any reader the file was not written with the current site in view, which undermines the rest of it.
The most common redirect in llms.txt files is also the easiest to fix: the file was written with http://example.co.uk/page
and the site now lives at https://www.example.co.uk/page/. Protocol, host name and trailing
slash each cause a hop. Write every link exactly as the page's canonical tag states it.
Checking links by hand with curl
To see exactly what a tool sees for one link, ask curl for the status code, the number of redirects and the final address:
curl -s -o /dev/null -L --max-time 20 \
-w "%{http_code} redirects=%{num_redirects} final=%{url_effective}\n" \
https://www.example.co.uk/services/
A healthy result looks like 200 redirects=0 final=https://www.example.co.uk/services/.
A result such as 200 redirects=2 means the page works but the link needs updating; 404 redirects=0
means the page is gone.
To run the same check over the whole file, extract the URLs from the Markdown links and loop over them:
curl -s https://www.example.co.uk/llms.txt \
| grep -oE '\]\(https?://[^) ]+\)' \
| sed -E 's/^\]\(//; s/\)$//' | sort -u \
| while read -r url; do
curl -s -o /dev/null -L --max-time 20 \
-w "%{http_code} %{num_redirects} %{url_effective} <- $url\n" "$url"
done
Any line that does not start with 200 0 needs attention. The loop uses a normal GET request
rather than HEAD, because some servers answer HEAD requests with 405 or 404 even when the page is fine,
which produces false alarms.
A fuller checker for noindex and canonical problems
Status codes miss the subtler problems: pages that answer 200 but carry a noindex directive, or whose
canonical tag points at a different URL. This Python script, which needs the requests package,
checks all three for every link in the file and prints only the entries that need work.
import re, sys, requests
FILE = sys.argv[1] if len(sys.argv) > 1 else "https://www.example.co.uk/llms.txt"
text = requests.get(FILE, timeout=20).text
urls = list(dict.fromkeys(re.findall(r"\]\((https?://[^)\s]+)\)", text)))
bad_md = re.findall(r"\]\((?!https?://)([^)]+)\)", text)
for u in bad_md:
print("NOT ABSOLUTE ", u)
for url in urls:
try:
r = requests.get(url, timeout=20, headers={"User-Agent": "llms-link-check"})
except requests.RequestException as e:
print("NO RESPONSE ", url, type(e).__name__)
continue
problems = []
if r.status_code != 200:
problems.append("status %d" % r.status_code)
if r.history:
problems.append("redirects to " + r.url)
html = r.text[:200000]
robots = r.headers.get("X-Robots-Tag", "") + " " + " ".join(
re.findall(r']+name=["\']robots["\'][^>]*>', html, re.I))
if "noindex" in robots.lower():
problems.append("noindex")
m = re.search(r']+rel=["\']canonical["\'][^>]*href=["\']([^"\']+)', html, re.I)
if m and m.group(1).rstrip("/") != r.url.rstrip("/"):
problems.append("canonical is " + m.group(1))
if problems:
print("FIX ", url, "|", "; ".join(problems))
Run it with python3 check_llms_links.py, or pass a local file path served through a test
server. The canonical check is deliberately simple: it expects the rel attribute before
href, so if your templates write them the other way round, adjust the pattern. Treat its
output as a to-do list, not a verdict, and confirm each item in a browser.
What to change for each finding
| Finding | Change in llms.txt | Change on the site |
|---|---|---|
| Redirect | Replace the link with the final URL | None; keep the redirect for other visitors |
| Chain or loop | Link to the final page | Point each old URL straight at the destination; break any loop |
| 404 or 410, page moved | Link to the new address | Add a single 301 from the old address |
| 404 or 410, page gone | Remove the entry, or link a page that now covers the topic | Consider a 301 to the closest replacement if one exists |
| 5xx | Leave it while you investigate; remove if it cannot be fixed quickly | Check server error logs for that URL and fix the template or timeout |
| Soft 404 | Remove or replace the entry | Make missing pages return a real 404 |
| Noindex | Remove the entry, unless the noindex is a mistake | If it is a mistake, remove the directive |
| Non-canonical | Use the canonical URL exactly | None, if the canonical tag is correct |
| 401 or 403 | Remove members-only pages | If the page is public, find the firewall rule refusing bots |
| Relative or malformed | Rewrite as a full https URL in [text](url) form | None |
A noindexed page is a judgement call. The file and the directive are sending opposite messages: one says "read this", the other says "do not put this in an index". Decide which is right. If the page is good enough to recommend, it is usually good enough to be indexed; if it is not, it does not belong in the file.
The fix, step by step
Step 1
Pull every URL out of the file
Run
the llms.txt Auditor for its list of failing links, then extract every URL yourself
from the live file with the grep command above, so you have the complete set rather than only the failures.
Include links in the ## Optional section and any in the summary or details paragraph. Note
any link written as a relative path such as /pricing/, or with broken Markdown syntax, since
those are faults in themselves.
Step 2
Record the status and final address of each
Run the curl loop or the Python checker and save the output in a spreadsheet with columns for the original URL, status code, number of redirects, final URL and any noindex or canonical problem. Use GET requests rather than HEAD, and run the check from outside your office network if you can, so that allow lists or cached responses on your own connection do not hide problems other visitors would meet.
Step 3
Replace redirected links with their destination
For every entry that redirected but ended at a working page, copy the final address into llms.txt, matching the canonical tag on that page exactly: the same protocol, host name, path and trailing slash, with no tracking parameters. Leave the redirects themselves in place on the server. Where a link passed through two or more hops, also update the old redirects so each one points directly at the final page.
Step 4
Repair or remove dead entries
For each 404 or 410, search the site for the page under a new address. If it moved, link the new URL and add a single 301 from the old one. If it was removed and another page now covers the subject, link that page and rewrite the note to match. Otherwise delete the entry. For 5xx errors, check the server error log for that URL and fix the fault before deciding whether the entry stays.
Step 5
Drop pages that are noindexed, blocked or not canonical
Look at every entry the checker flagged for noindex, a canonical pointing elsewhere, a login prompt or a 403. Swap non-canonical links for the canonical URL. Remove members-only pages. For noindexed pages, decide whether the directive or the entry is the mistake and fix whichever is wrong. If public pages return 403 only to scripts, investigate the firewall rather than editing the file.
Step 6
Add a link check to your publishing routine
Upload the corrected file, open it in a private window to confirm the new version is served, and re-run the llms.txt Auditor. Then add the checker script to your build, deploy or a weekly scheduled job so it runs against the live file and reports any entry that does not return a direct 200. Add a step to your page-removal routine too: whoever deletes or renames a page also updates llms.txt.
Edge cases that cause false alarms
- Bot protection. A CDN or security plugin may challenge scripted requests with a 403, a 503 or a JavaScript page while browsers pass. Check the same URL in a browser; if it loads, the link is fine but crawlers may have a problem, which the AI crawler access guide covers.
- Rate limiting. Checking a long file quickly can trigger 429 responses. Add a short pause between requests and re-test only the failures.
- Language or country redirects. A site that redirects based on the visitor's location or browser language will send your checker somewhere different from a crawler in another country. Link to the specific language version, and see the language guide for multilingual files.
- Fragments. A link such as
/faq/#returnsis fetched as/faq/; servers never see the part after the hash. The checker cannot tell whether the anchor still exists, so check those in a browser. - Case and encoding. Paths are case-sensitive on most servers, so
/About/and/about/may differ. Spaces and non-ASCII characters in URLs should be percent-encoded in the file. - External links. Links to your own subdomains, a status page or a code repository are fine, but they break for reasons outside your site's control. Check them on the same schedule.
Stopping links breaking again
Links in llms.txt break for the same reason links anywhere break: pages change and nobody updates the places that point at them. The difference is that llms.txt is a single file you control, so it is easy to protect.
- Generate the URLs, write the notes. If your CMS or build tool can produce the list of URLs from page IDs, links update themselves when slugs change, and you only maintain the notes.
- Fail the build on a bad link. For static and framework sites, make the checker exit with an error when it finds a problem, so a broken entry never reaches production.
- Treat llms.txt as an inbound link source during migrations. When you plan redirects for a redesign or a domain move, include every URL in the file in the redirect map and update the file on launch day.
- Watch the auditor after big changes. Re-run it after a migration, a change of permalink structure or a new theme.
Broken links are one symptom of a file drifting away from the site. If whole sections now describe pages or services that no longer exist, the stale llms.txt guide covers rebuilding the file around the current site.
1. Does a redirect in llms.txt count as a broken link?
Not strictly, because most tools follow redirects and still reach the page. It is still worth fixing. Each redirect costs an extra request, some fetchers limit how many hops they follow or refuse redirects to another host, and a file full of old addresses suggests it is out of date. Replace the link with the final URL exactly as the page canonical tag gives it, and leave the redirect in place on the server for everyone else.
2. Should I list pages that are marked noindex?
Generally no. A noindex directive asks search engines to keep the page out of their index, while listing it in llms.txt recommends it as one of your best pages, so the two send opposite messages. Decide which is right. If the page is useful enough to recommend, remove the noindex. If it is thin, private or a duplicate, remove it from the file instead. Occasionally a utility page is noindexed for good reason; weigh it case by case.
3. Can I use relative links in llms.txt?
It is better not to. The file is often read outside the context of your site, for example pasted into a tool or loaded by an agent that has not recorded where it came from, and a relative path such as /pricing/ then means nothing. The examples in the proposal use full URLs. Write every link as an absolute https address on your canonical host name, exactly as it appears in the page canonical tag.
4. Why does the auditor say a link is broken when it works in my browser?
Usually because your browser and the auditor are treated differently. Your browser carries cookies, may be logged in, and passes bot challenges that scripts cannot. A firewall or security plugin may return 403 or 503 to automated requests, or a page may only work for signed-in users. Test the URL in a private window, then with curl. If only the scripted request fails, the page itself is fine but crawlers are being refused.
5. How often should I check the links in llms.txt?
Check them every time you publish the file and at least weekly for an active site. Links break when other people change pages, not only when you change the file, so a scheduled check catches problems between edits. For static and framework sites, running the check as part of each build is the most reliable approach. For a small site that changes rarely, a monthly check and a check after any redesign or migration is usually enough.
6. What if a linked page returns a 500 error only sometimes?
Intermittent server errors are real problems, and a crawler that meets one may give up on the page until its next visit. Look in your server error log for that URL at the times the check failed, and check whether the page is slow to generate, since timeouts often surface as 502, 503 or 504 errors. Caching the page, fixing a slow query or raising a timeout usually cures it. Keep the entry only if you can make it reliable.
7. Do links in llms-full.txt need checking too?
Yes, if you publish one. The full-text file usually includes a source URL for each page and keeps the links inside the page text, and those break in the same ways. If you generate llms-full.txt from the links in llms.txt, fixing the index and regenerating the full file fixes the source lines automatically. Links within the body text come from the pages themselves, so a site-wide broken link check covers them.
The llms.txt Auditor fetches your /llms.txt and /llms-full.txt files, validates them against the proposed format, checks that each linked URL responds, confirms robots.txt allows the relevant AI crawlers and scores the quality of the content.
Open llms.txt Fixes โ