Overview

/llms.txt is a markdown index at the site root that points agents at a site’s best pages; /llms-full.txt is the optional companion with page bodies inlined. Generate both from page frontmatter in the build so they cannot drift. Both are community conventions: Google Search ignores llms.txt, so treat them as a convenience for agents that choose to read them. The format reference is llms-txt.

Prerequisites

  • Stable URLs; the file lists absolute URLs that should not move.
  • Frontmatter with title, summary, category, and status on every page.
  • A build step that can write to the output root (Quartz copies non-Markdown files from content/; Astro and Next.js use public/; Hugo uses static/).

Steps

1. Follow the format

Per llmstxt.org, the H1 with the site name is the only required element. Add a blockquote summary, then H2 sections of - [Title](absolute-url): one-sentence note bullets. An H2 named Optional marks links an agent can skip when context is tight.

# Site Name
 
> Opinionated reference vault of best practices for web development and AI agent work.
 
## Coding
 
- [TypeScript](https://example.com/coding/typescript): When to use strict mode, utility types, and runtime validation.
 
## Optional
 
- [Glossary](https://example.com/glossary/): Short term definitions.

Write each note as a routing hint (what will I find here), one sentence. The spec also proposes a clean .md version of each page at the page URL plus .md; link to those when the site serves them.

2. Generate both files in the build

// scripts/build-llms.mjs  (npm i -D gray-matter)
import fs from "node:fs/promises"
import path from "node:path"
import matter from "gray-matter"
 
const BASE = "https://example.com"
const OUT = "content"   // Quartz copies non-Markdown files from content/; Astro and Next.js use public/, Hugo static/
const files = (await fs.readdir("content", { recursive: true })).filter((f) => f.endsWith(".md"))
const pages = []
for (const f of files) {
  const { data, content } = matter(await fs.readFile(path.join("content", f), "utf8"))
  if (data.status !== "stable") continue
  pages.push({ ...data, url: `${BASE}/${f.replace(/\.md$/, "").replace(/(^|\/)index$/, "$1")}`, body: content.trim() })
}
const byCat = Object.groupBy(pages, (p) => p.category ?? "misc")
 
const index = [`# Site Name`, ``, `> One-sentence site summary.`, ``]
for (const [cat, items] of Object.entries(byCat).sort()) {
  index.push(`## ${cat}`, ``)
  for (const p of items.sort((a, b) => a.title.localeCompare(b.title))) {
    index.push(`- [${p.title}](${p.url}): ${p.summary}`)
  }
  index.push(``)
}
await fs.writeFile(`${OUT}/llms.txt`, index.join("\n"))
await fs.writeFile(
  `${OUT}/llms-full.txt`,
  pages.map((p) => `# ${p.title}\n\nSource: ${p.url}\n\n${p.body}`).join("\n\n---\n\n"),
)

Filter on status so drafts and deprecated pages never leak; the index replace maps folder MOCs to /folder/ instead of a 404 at /folder/index. Run the script from prebuild or an explicit CI step; npx quartz build does not trigger npm hooks. Write llms-full.txt with the full corpus at any size, llms-context.txt compressed under 240k tokens, and one llms-full/<category>.txt per top-level folder (llms-txt); single-question agents route through llms.txt instead.

3. Serve from the root

The files must resolve at https://yourdomain.com/llms.txt and /llms-full.txt, not under a subpath. Plain text/plain or text/markdown is fine. Publish /ai.txt beside it if you want to state AI-use terms (ai-txt). Optionally reference the index from robots.txt as a comment; crawlers ignore comments, so it only helps people and tools that read the file. Keep the file fresh at the CDN with a short Cache-Control.

Verify it worked

curl -sI https://yourdomain.com/llms.txt | head -1          # HTTP/2 200
curl -s https://yourdomain.com/llms.txt | head -3           # "# " then "> "
curl -s https://yourdomain.com/llms.txt | grep -oE 'https?://[^)]+' | sort | uniq -d   # empty: no duplicates
curl -s https://yourdomain.com/llms.txt | grep -oE 'https?://[^)]+' | sort -u | head -3 \
  | xargs -I {} curl -sI {} -o /dev/null -w "%{http_code} {}\n"                       # 200s

Common errors

  • File lands at /static/llms.txt. Move it to the build root.
  • Relative links. Agents fetch the file in isolation; use absolute URLs.
  • A page listed twice across categories. Give each page one category.
  • Notes run to a paragraph. Cut them to one routing sentence.
  • Frontmatter breaks a hand-rolled parser (arrays, multi-line values). Use a real YAML parser such as gray-matter.

FAQ

Does Google use llms.txt for ranking?

No; Google Search ignores llms.txt. Treat it as a community convention and a convenience for agents that choose to read it.

Where should llms.txt live on my site?

At the site root, resolving at https://yourdomain.com/llms.txt, not under a subpath such as /static/.

Use absolute URLs, because agents fetch the file in isolation.

Do I need llms-full.txt as well as llms.txt?

Yes; ship /llms-full.txt (full corpus, any size), /llms-context.txt (whole site in one window), and per-category /llms-full/<category>.txt slices. Agents answering one question route through /llms.txt.