/ llmtxt.info

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.

static approach — public/llms.txt
# 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
// 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 from src/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-8 in 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
// 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

verify
# 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.

Full creation guide · Next.js guide · WordPress guide

Sources