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.

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.