How Do You Implement llms.txt in 2026?

Version 2 of the llms.txt proposal landed in August 2026: it added link relations so agents can find the file, and dropped the context-expansion tooling that gave the Optional section its meaning. This page gives you the exact file structure, three ways to ship it, and the failure modes that make an agent skip it.

CategoryCrawler access, licensing & indexing StatusDe facto standard Maintained byJeremy Howard / community Glippy checkMachine Readability (category 6)

llms.txt (/llms.txt) is a markdown file published at your site root, or at any subpath within it, that gives an agent a one-paragraph summary of the site plus a curated list of links to the pages worth reading. It is built for inference time rather than training: an agent that needs to answer a question about your product reads the index, then fetches only the linked pages. It is a community proposal maintained on GitHub, not a W3C or IETF standard, and no search engine treats it as a ranking input.

Why llms.txt matters for AI visibility

Be honest about who reads this file, because it decides whether shipping one is worth your afternoon. Google Search states plainly that it does not use llms.txt for AI Overviews, AI Mode or anything else, and that publishing one will "neither harm nor help" your visibility there. Server-log studies through 2026 keep finding that GPTBot, ClaudeBot and PerplexityBot request HTML pages almost exclusively and hit /llms.txt in negligible volume. Where the file demonstrably earns its keep is the other half of the agentic web: coding assistants and chat agents pointed at a URL, documentation platforms that generate the file for every site they host, and MCP servers that fetch and search it on demand. If you want the background on how the convention got here and how it compares to robots.txt, the conceptual explanation lives here: what is llms.txt.

The practical argument is token economy and control. An agent that lands on your marketing site and has to reverse-engineer it from HTML burns context on navigation, cookie banners and scripts, then gives up or guesses. A good llms.txt is a few hundred tokens that says what the site is and which twenty pages answer real questions, which means the agent spends its budget on your content instead of your chrome. That is also why the AI labs publish one for their own developer docs, and why Chrome's Lighthouse now includes an llms.txt audit in its agentic browsing checks: the audit passes when the file loads cleanly, is not applicable when it 404s, and fails when the server errors on it.

Where the spec lives

Everything on this page is checked against these sources. The spec itself is a fifteen minute read and worth doing in full before you write the file.

Three ways to implement llms.txt

These three stack rather than compete. Hand-write the file if fewer than about thirty of your pages are worth an agent's attention; generate it from your content index once the list no longer fits in your head; and wire up the v2 link relations either way, so an agent that lands on a deep page can find the index without guessing at the root.

01

Hand-write the file at the site root

The minimum viable version, and the one most sites should ship first. The order of the sections is fixed by the spec: byte-order mark if you must, then an H1, then a blockquote summary, then any non-heading markdown, then H2 sections containing link lists. Only the H1 is strictly required, but a file with no links gives an agent nothing to follow.

markdown/llms.txt
# Acme Analytics

> Acme Analytics is a self-hosted event analytics server with a REST ingest API and a SQL query layer. This file indexes the documentation an agent needs to integrate it. Every link points to a clean markdown version of the page.

Notes:

- The API is versioned by path. `/v2/` is current, `/v1/` is frozen and gets security fixes only.
- Self-hosted and cloud share one API surface. The cloud base URL is `https://api.acme.example`.

## Docs

- [Quickstart](https://docs.acme.example/quickstart.md): Install, create an API key, send a first event
- [Ingest API reference](https://docs.acme.example/api/ingest.md): Endpoints, payload schema, batching and rate limits
- [SQL query layer](https://docs.acme.example/api/query.md): Query syntax, materialised views, export formats

## Guides

- [Self-hosting on Docker](https://docs.acme.example/guides/docker.md): Compose file, volumes and the upgrade path
- [Migrating from v1](https://docs.acme.example/guides/migrate-v1.md): Field renames and the compatibility shim

## Optional

- [Changelog](https://docs.acme.example/changelog.md): Release notes back to 1.0
- [Architecture notes](https://docs.acme.example/internals.md): Storage engine and query planner internals

What this does: An agent asked "how do I batch events into Acme?" reads roughly 250 tokens, sees that the ingest reference covers batching, and fetches exactly one more URL. The blockquote does the heavy lifting, because it is the only part guaranteed to be read before the agent decides whether to follow any link at all.

02

Generate it from your content index at build time

Use this once the file is large enough that hand-editing it will drift. The source of truth is whatever already describes your pages: a frontmatter collection, a docs config, a CMS query. The script below takes a flat list and emits a spec-shaped file, keeping the Optional section last and pointing every link at a markdown twin.

javascriptscripts/build-llms-txt.mjs
import { writeFile, mkdir } from 'node:fs/promises';

const SITE = 'https://docs.acme.example';
const TITLE = 'Acme Analytics';
const SUMMARY = 'Acme Analytics is a self-hosted event analytics server with a REST '
  + 'ingest API and a SQL query layer. Every link below is a markdown version of the page.';

// Replace this literal with your real content index. Order matters: an agent
// reads top down and stops once it has enough context.
const pages = [
  { section: 'Docs', title: 'Quickstart', path: '/quickstart', note: 'Install, create an API key, send a first event' },
  { section: 'Docs', title: 'Ingest API reference', path: '/api/ingest', note: 'Endpoints, payload schema, batching, rate limits' },
  { section: 'Guides', title: 'Self-hosting on Docker', path: '/guides/docker', note: 'Compose file, volumes and the upgrade path' },
  { section: 'Optional', title: 'Changelog', path: '/changelog', note: 'Release notes back to 1.0' },
];

// Preserve first-seen section order, but always sink "Optional" to the bottom.
const sections = [...new Set(pages.map((p) => p.section))]
  .sort((a, b) => (a === 'Optional') - (b === 'Optional'));

let out = `# ${TITLE}\n\n> ${SUMMARY}\n`;
for (const name of sections) {
  out += `\n## ${name}\n\n`;
  for (const p of pages.filter((x) => x.section === name)) {
    out += `- [${p.title}](${SITE}${p.path}.md): ${p.note}\n`;
  }
}

await mkdir('public', { recursive: true });
await writeFile('public/llms.txt', out, 'utf8');
console.log(`wrote public/llms.txt: ${sections.length} sections, ${pages.length} links`);

What this does: It makes the index a build artefact rather than a document someone forgets. Wire it into your build step and add a follow-up job that requests every generated URL, so a renamed page fails CI instead of sending an agent to a 404.

03

Advertise it with v2 link relations

This is the part v2 added, and the part almost nobody has shipped yet. An agent that arrives on a deep page has no way to know a markdown twin or an index exists. The spec answers with two standard relations: rel="alternate" type="text/markdown" for the markdown version of a page, and rel="describedby" for the llms.txt that covers it. The HTTP header form is the stronger one, because it also works for non-HTML resources and needs no template edits.

nginx/etc/nginx/conf.d/acme-docs.conf
server {
  listen 443 ssl;
  server_name docs.acme.example;
  root /srv/docs;

  # Every HTML page points at its markdown twin and at the index that covers it.
  location ~ ^/(.*)\.html$ {
    add_header Link '</$1.html.md>; rel="alternate"; type="text/markdown"' always;
    add_header Link '</llms.txt>; rel="describedby"' always;
    try_files $uri =404;
  }

  # Markdown twins must not be served as octet-stream, or agents will skip them.
  location ~ \.md$ {
    default_type text/markdown;
    add_header Link '</llms.txt>; rel="describedby"' always;
    try_files $uri =404;
  }

  # The index itself: markdown content type, short cache, no HTML fallback.
  location = /llms.txt {
    default_type text/markdown;
    add_header Cache-Control "public, max-age=300" always;
    try_files $uri =404;
  }
}

What this does: It turns discovery from a guess into a lookup. The equivalent in a page template is <link rel="alternate" type="text/markdown" href="/api/ingest.html.md"> plus <link rel="describedby" href="/llms.txt">, but the header version covers the markdown files themselves and can be set at the CDN without touching any page.

Implementation guidelines

These are the things that go wrong after the file is live.

  1. Lead with the H1 and the blockquote. The H1 is the only required section and should be the project or site name, not a slogan. The blockquote is the one line an agent is guaranteed to read, so put the disambiguating facts there: what the product is, which version is current, what it is not.
  2. Link to markdown, not to HTML. v2 states the expectation directly: agents view or search llms.txt, then follow links that should point at LLM-friendly content. Publish a twin at page.html.md or page.md and link to that, otherwise you have handed the agent the same HTML parsing problem the file was meant to solve.
  3. Serve it as text, never as HTML. The most common production failure is a soft-404: a missing file that returns your styled 404 page with a 200 status. Assert on the content type and on the first byte being #, not just on the status code.
  4. Stop treating Optional as a machine instruction. In v1 the Optional section told the llms_txt2ctx expander what to omit. v2 dropped that tooling and with it the mechanical meaning, so Optional is now purely a human convention for secondary links. Do not build logic that depends on an agent honouring it.
  5. Scope subpath files deliberately. A file covers the URLs under its path and the most specific file wins, so /docs/llms.txt covers /docs/ only. This is how a project that controls a path but not the origin root, such as a GitHub Pages site, can still participate.
  6. Regenerate and re-validate on every content change. A stale index is worse than none: it sends agents to dead URLs and they stop trusting the file. Fetch every linked URL in CI and fail the build on anything that is not a 200 with a text content type.
  7. Cache it short and version nothing. Five minutes of edge cache is plenty. Do not add query strings, dated filenames or content negotiation to the index URL, because the whole value of the convention is that /llms.txt is guessable.

Do this, not that

Do

  • Return 200 with text/markdown or text/plain, and a body whose first line is a single # heading.
  • Point every link at a .md twin that actually resolves, and prove it in CI.
  • Advertise the file with rel="describedby", as an HTML <link> or an HTTP Link: header.
  • Put /docs/llms.txt beside the docs it describes when you only control a path.

Do not

  • Do not paste your sitemap into it. A dump of 4,000 URLs with no notes defeats the entire point.
  • Do not let the server answer a missing file with your HTML 404 page under a 200 status.
  • Do not use it for access control. robots.txt and Content-Signal handle permission, llms.txt handles curation.
  • Do not promise anyone it will move Google Search rankings, because Google Search says it ignores the file.

How Glippy checks this

Glippy fetches /llms.txt and scores it under Machine Readability, category 6, as a bonus check worth up to 15 points. It parses the body the way the spec is written: the first # line becomes the title, ## lines are counted as sections, and [text](url) matches are counted as links. A file with a title and at least one link scores the full 15. A file that parses but is sparse, no title or no links, drops to 8 with a warning. A body that looks like HTML is reported as a likely soft-404 and scores 4, which is the single most useful signal here because the file looks fine in a browser. A missing file is reported as information only and costs you nothing. Glippy's own file is a live worked example at glippy.dev/llms.txt, and the full category breakdown is on the machine readability checker page.

Check your llms.txt setup

Glippy runs 240+ checks across 16 categories on any page, including Machine Readability (category 6). No sign-up required.

Frequently asked questions

Not directly, and you should say so out loud when someone asks. Google's own AI optimization guide states that Search does not use these files and that publishing one will neither harm nor help visibility, and server-log analyses through 2026 keep showing that GPTBot, ClaudeBot and PerplexityBot request HTML pages rather than /llms.txt. The file earns its place with agents that are pointed at your site on purpose: coding assistants reading your API docs, chat agents fetching a URL a user pasted, and MCP servers built to search llms.txt files. Treat it as agent ergonomics, not as a ranking factor.

Version 2 was published in August 2026 and made four changes. It added standard link relations for discoverability, rel="alternate" type="text/markdown" and rel="describedby", usable as HTML link elements or as an HTTP Link: header. It allowed both markdown twin URL forms, page.html.md and page.md, where v1 allowed only the first. It defined what a subpath file means: it covers the pages under its path, and the most specific file applies. And it dropped the llms_txt2ctx context-expansion tool from the proposal, which removed the mechanical meaning of the Optional section.

The conventional location is /llms.txt at the site root, but the spec explicitly allows a file at any path, and a site can have several. Each file covers the URLs beneath its own path, so /docs/llms.txt describes everything under /docs/, and where more than one file applies an agent should use the most specific one. That is deliberate: it lets a project that controls only a subdirectory, such as a GitHub Pages project site, publish a valid file without access to the origin root. The spec author's reference file is served from /docs/ rather than the root for exactly this reason.

No. Neither v1 nor v2 of the llms.txt proposal defines a file called llms-full.txt, and the spec text does not mention it. It is a separate community convention, popularised by documentation platforms that concatenate an entire docs set into one large markdown file so an agent can ingest it in a single fetch. The two are complementary rather than alternatives: llms.txt is a small curated index designed to fit in context, while llms-full.txt is the bulk payload behind it, and a site can publish either, both or neither.

Reviewed against the primary sources on . These standards move quickly, so check the linked specs before you ship.

Check your site for AI search readiness

Start free with the Glippy Chrome extension for instant page checks. Scaling up? Automate audits across many URLs and your whole sitemap with the Glippy MCP server.

Add Glippy to Chrome – free Automate with the MCP server →