llms.txt with Astro
Two approaches: drop a file in public/ for instant deployment, or generate it dynamically from your content collections with a TypeScript endpoint.
Last updated:
Approach 1, Static file in public/
The simplest option. Create a plain text file at public/llms.txt in your project root.
Astro copies the entire public/ directory verbatim to dist/ during astro build, so the file is served at
/llms.txt with zero configuration.
# Place the file at: public/llms.txt
# Astro copies everything in public/ to the output directory as-is.
# Result: served at https://yoursite.com/llms.txt, no code changes needed.
# After deploy, verify:
curl -I https://yoursite.com/llms.txt
# Expected: HTTP/2 200 | Content-Type: text/plain When to use this: your content doesn’t change often, you want zero build-time overhead, or you maintain the file manually. Works identically on Cloudflare Pages, Vercel, Netlify, and any other Astro host.
Approach 2, TypeScript endpoint with getCollection()
Astro matches the double extension .txt.ts and treats
src/pages/llms.txt.ts as an API endpoint that responds at
/llms.txt. In SSG mode (the default), Astro pre-renders it to a static dist/llms.txt at build time, no runtime cost.
// src/pages/llms.txt.ts
// Astro treats src/pages/foo.ext.ts as a route for /foo.ext
// In SSG mode (default), it pre-renders to dist/llms.txt at build time.
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';
export const GET: APIRoute = async () => {
// Replace 'docs' with your actual content collection name
const docs = await getCollection('docs');
const lines = [
'# My Site',
'',
'> One-sentence description of what this site is about.',
'',
'## Documentation',
'',
...docs
.filter((d) => !d.data.draft)
.sort((a, b) => (a.data.order ?? 999) - (b.data.order ?? 999))
.map((d) => `- [${d.data.title}](https://yoursite.com/${d.slug}/): ${d.data.summary ?? ''}`.trimEnd()),
'',
'## Optional',
'',
'- [Changelog](https://yoursite.com/changelog/): version history.',
];
return new Response(lines.join('\n'), {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
};
Key points:
-
Replace
'docs'with your actual collection name fromsrc/content/config.ts. - Filter out drafts before mapping:
!d.data.draft. -
Each line annotation (
: short note) is optional but strongly recommended , it gives LLMs context about each page without them having to fetch it. - Always set
Content-Type: text/plain; charset=utf-8in the response.
Adding llms-full.txt
The llms-full.txt companion file inlines the full body of every page, useful for RAG pipelines and LLM clients that want the entire corpus at once. Create a parallel endpoint:
// src/pages/llms-full.txt.ts
// Generates the full-content companion file.
// Each entry gets its full Markdown body inlined, useful for RAG pipelines.
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';
export const GET: APIRoute = async () => {
const docs = await getCollection('docs');
const sections: string[] = [];
for (const doc of docs.filter((d) => !d.data.draft)) {
sections.push(
`# ${doc.data.title}`,
'',
`URL: https://yoursite.com/${doc.slug}/`,
'',
doc.body, // raw Markdown, no HTML, ideal for LLMs (Astro 4+)
'',
'---',
'',
);
}
return new Response(sections.join('\n'), {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
};
doc.body contains raw Markdown (available since Astro 4), which is ideal for LLMs: no
HTML tags, clean semantic text. For Astro 3, use the
render() helper and strip tags with a simple regex.
Verify after build
# Build the project
npx astro build
# Check static output (works for both approaches in SSG mode)
cat dist/llms.txt
# Or start preview server and curl:
npx astro preview &
curl http://localhost:4321/llms.txt
curl http://localhost:4321/llms-full.txt
After deploying, paste your live URL into the validator
to confirm spec compliance: exactly one H1, valid link syntax (- [title](https://...)), all URLs absolute, no empty sections.
Which approach to choose
- Static
public/llms.txt, best for sites updated infrequently. Zero overhead, no TypeScript required, works everywhere. - TypeScript endpoint
src/pages/llms.txt.ts, best for documentation sites with many auto-generated pages. The file stays in sync automatically on every build without manual edits.