Skip to content

The site-wide files

Three files, three lines each.

app/llms.txt/route.ts
import { createGeoffyTextRoute } from "@geoffy/headless/next";
export const GET = createGeoffyTextRoute({ siteKey: process.env.GEOFFY_SITE_KEY! }, "llms.txt");
app/llms-full.txt/route.ts
import { createGeoffyTextRoute } from "@geoffy/headless/next";
export const GET = createGeoffyTextRoute({ siteKey: process.env.GEOFFY_SITE_KEY! }, "llms-full.txt");
app/agents.md/route.ts
import { createGeoffyTextRoute } from "@geoffy/headless/next";
export const GET = createGeoffyTextRoute({ siteKey: process.env.GEOFFY_SITE_KEY! }, "agents.md");

A rewrite in next.config.js works, but it must be in the beforeFiles array.

If your app has a catch-all segment such as app/[lang], a rewrite registered anywhere else loses to it, and the catch-all answers /llms.txt with your home page and HTTP 200, which a crawler reads as a successful answer rather than a missing file.

Route handlers cannot lose that race. app/llms.txt/route.ts is a static route segment, and static segments beat dynamic ones with no configuration.

The fetch is cached for revalidateSeconds, one hour by default, under the helper’s own cache tag:

createGeoffyTextRoute({ siteKey, revalidateSeconds: 300 }, "llms.txt");

Mounting the revalidate route purges that tag on publish; see how updates arrive.

A third argument makes the route tell Geoffy which AI agent fetched the file, by its agent name and never a raw user agent:

createGeoffyTextRoute({ siteKey }, "llms.txt", { forwardCrawler: true });

It is off by default for a reason. It reads the request headers, which makes Next render the route on every request instead of serving it from its static or cached output. Turn it on if you want these reads counted without the AI visit middleware.

The route answers 503 with a Retry-After header, never a fabricated 404. A crawler retries a 503; a 404 tells it to drop the page from its index, which is a much more expensive mistake to make on your behalf.