Agent ReadySign in

How to add an llms.txt file to a Next.js site

A step-by-step guide for both static public/llms.txt and dynamic (route handler) approaches in Next.js 13+.

Last updated

What is llms.txt?

llms.txt is a Markdown file served at /llms.txt that gives AI systems a curated, plain-text map of a website’s most important content. The format was proposed in late 2024 by Jeremy Howard at Answer.AI. For the full validation spec, see our llms.txt checker.

Where should llms.txt live in a Next.js app?

Two valid locations, depending on whether your content is static or generated. For most sites, the static path is correct: drop a file at public/llms.txt and Next.js serves it at the site root with the correct text/plain Content-Type. For sites where the page list is generated — from a CMS, a database, or a build-time index — use a route handler at src/app/llms.txt/route.ts. The five steps below cover the static path; the dynamic fork is described after.

Step 1 — Create the public/llms.txt file

From your project root, create the file under public/. Next.js auto-serves anything in this folder at the site root, so the file becomes available at /llms.txt the moment it’s on disk — no route, no build step, no configuration change.

# from your Next.js project root
touch public/llms.txt

Step 2 — Add the required Markdown structure

The llmstxt.org spec mandates two elements: an # H1 with your site name, and a blockquote summary one or two sentences long. After that, add ## H2 section headings to group your links. Sections are optional but recommended for any site with more than a handful of pages.

Step 3 — List your most authoritative pages

Under each section heading, add Markdown links in the format - [Title](URL): description. Curate to your most valuable pages — documentation, API reference, policies, key product pages. Quality over quantity: AI systems prefer a short, high-signal index over a full sitemap. Aim for 10–50 links.

# Acme Cloud Widgets

> Widgets-as-a-service for serverless apps. Deploy in one command.

## Documentation

- [Getting started](https://acme.dev/docs/start): Zero to deployed in 60 seconds
- [API reference](https://acme.dev/docs/api): REST endpoints and authentication
- [SDKs](https://acme.dev/docs/sdks): Official client libraries

## Policies

- [Privacy policy](https://acme.dev/privacy): Data handling and retention
- [Terms of service](https://acme.dev/terms): Usage terms

## Optional

- [Sitemap](https://acme.dev/sitemap.xml)
- [llms-full.txt](https://acme.dev/llms-full.txt): Full content bundle

An optional llms-full.txt companion file concatenates the full content of those URLs into one Markdown bundle — useful for docs-heavy sites where the index alone doesn’t carry enough context.

Step 4 — Verify it serves correctly

Deploy or start your dev server, then send a HEAD request to confirm the file returns HTTP 200 with a text/plain Content-Type. Next.js sets this header automatically for .txt files under public/; no configuration is needed.

curl -I https://yoursite.com/llms.txt

# Expected response:
# HTTP/2 200
# content-type: text/plain; charset=utf-8

Step 5 — Validate against the llmstxt.org spec

Run your URL through the llms.txt checker to confirm the file passes the 9-check compliance suite — H1 present, blockquote summary, valid Markdown, correct link format, accessible URLs, proper Content-Type, and the optional llms-full.txt companion. Failed checks come with a one-line fix each.

How do I generate llms.txt dynamically?

When your page list comes from a database, CMS, or build-time index, use a Next.js route handler at src/app/llms.txt/route.ts. Return a Response with an explicit Content-Type: text/plain header and the Markdown body assembled from your data source. File-system routes win when both exist, so delete public/llms.txt if you switch to the handler.

// src/app/llms.txt/route.ts
import { getPublishedPages } from "@/lib/content";

export async function GET() {
  const pages = await getPublishedPages();

  const body = [
    "# Acme Cloud Widgets",
    "",
    "> Widgets-as-a-service for serverless apps.",
    "",
    "## Documentation",
    ...pages.map((p) => `- [${p.title}](${p.url}): ${p.description}`),
  ].join("\n");

  return new Response(body, {
    headers: {
      "Content-Type": "text/plain; charset=utf-8",
      "Cache-Control": "public, max-age=3600",
    },
  });
}

The route handler example targets Next.js 13+ (App Router). The static-file path works on the Pages Router with the same public/llms.txt location.

Common pitfalls when adding llms.txt

  • Pages behind authentication. Links in llms.txt must resolve without a session. Public docs, policies, and marketing pages only.
  • Wrong Content-Type. If your CDN or middleware rewrites .txt to text/html, many validators reject the response.
  • JS-only navigation targets. AI crawlers fetch URLs without running JavaScript; if a linked page’s primary content requires a hydrated app, the link is effectively dead.
  • Missing blockquote. The > summary line directly under the H1 is mandatory; without it, strict validators fail the file.
  • Stale links. Linked URLs that 404 or redirect off-domain hurt your citation quality. Re-check after every site restructure.

Frequently asked questions

Does llms.txt need to live at the site root?
The canonical path is /llms.txt at the root of your domain. Some validators also accept /docs/llms.txt or /.well-known/llms.txt, but the root path is what most AI clients try first. Publish at the root; add alternate paths only if you've already shipped them.
Can I generate llms.txt dynamically with Next.js?
Yes. Create a route handler at src/app/llms.txt/route.ts that returns a Response with Content-Type: text/plain. Build the Markdown from your CMS, database, or sitemap source. Note: file-system routes (public/llms.txt) win when both exist — delete the static file if you want the route handler to take over.
Do I need an llms-full.txt file too?
Not required, but it helps for docs-heavy sites. llms-full.txt is the optional companion that concatenates the full text of every page in your llms.txt into a single Markdown file. AI clients fetch it when they want more context than the index alone provides. Same Content-Type and location rules apply.
Does my llms.txt need a special Content-Type header?
It should be served with Content-Type: text/plain (or text/markdown). Next.js sets text/plain automatically for .txt files under public/. If you generate it dynamically, set the header explicitly in your Response. Avoid text/html — most validators treat that as a misconfiguration.
How do I keep my llms.txt up to date as my site grows?
For static files, edit and redeploy. For dynamic generation, the route handler regenerates on every request (or on a configurable cache window via Cache-Control). For high-velocity sites, hook the regeneration into your sitemap or content-publish flow so llms.txt stays in sync without manual touch.