Skip to content

The site-wide files

Three files, three lines each. All three are written out below on purpose: the shape is easy to get subtly wrong when it is left as “repeat for the other two”.

src/pages/llms.txt.ts
import { createGeoffyTextEndpoint } from "@geoffy/headless/astro";
export const GET = createGeoffyTextEndpoint(
{ siteKey: import.meta.env.GEOFFY_SITE_KEY },
"llms.txt",
);
src/pages/llms-full.txt.ts
import { createGeoffyTextEndpoint } from "@geoffy/headless/astro";
export const GET = createGeoffyTextEndpoint(
{ siteKey: import.meta.env.GEOFFY_SITE_KEY },
"llms-full.txt",
);
src/pages/agents.md.ts
import { createGeoffyTextEndpoint } from "@geoffy/headless/astro";
export const GET = createGeoffyTextEndpoint(
{ siteKey: import.meta.env.GEOFFY_SITE_KEY },
"agents.md",
);

src/pages/llms.txt.ts is a real file, so a catch-all such as src/pages/[...lang].astro cannot swallow it. That matters more than it sounds: a catch-all answering /llms.txt with your home page returns HTTP 200, which a crawler reads as a successful answer rather than a missing file.

robots-rules.txt is deliberately not accepted here

Section titled “robots-rules.txt is deliberately not accepted here”

createGeoffyTextEndpoint refuses it. Every other name serves a whole file; the robots rules are a fragment you append to your own. Serving them as a whole file would replace your rules with ours. See crawler rules.

The endpoint answers with Cache-Control: max-age=300, so a browser or CDN in front of your site can keep a copy for 5 minutes. revalidateSeconds has no effect in Astro. On a prerendered site the file is read once, at build time, so your next build is the refresh — see how updates arrive.

The endpoint 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.