In Practice

Publishing the files on your platform

Writing the files is the easy part. Getting them served correctly from your domain root, with the right Content-Type, is where implementations quietly fail — a perfectly valid file served as the wrong type is an unreadable file.

Check what you are actually serving

Before anything else, publish one file and look at the response. The specifications require llms.txt to be served as text/plain; charset=utf-8, reachable without authentication, returning 200 over HTTPS.

verify
curl -sSI https://example.com/llms.txt | grep -i '^content-type\|^HTTP'

You want HTTP/2 200 and content-type: text/plain; charset=utf-8. If you see application/octet-stream, a missing charset, or a 404 that returns your styled error page with a 200 status, fix that before writing another file.

Lighthouse checks this too

Chrome DevTools' Lighthouse includes an llms.txt audit under its Agentic browsing category, so you can verify from the browser as well as the command line. Note how it grades: a 404 is reported as Not Applicable, because publishing the file is optional at present — but a server error when fetching it is flagged. A passing audit therefore means the file was retrievable, not that its contents are right.

The soft-404 trap

Many hosts serve a friendly “page not found” HTML page with a 200 status rather than a 404. To a consumer, that looks like a valid llms.txt containing your error page. Always check the status code, not just whether something came back.

Per-platform

Every file goes at the domain root — https://example.com/llms.txt, not in a subdirectory. What differs is how your platform gets it there.

Cloudflare Pages

Static host

Put the files in your output directory (or in public/ if your framework copies it through).

llm.txt redirect

text
# _redirects
/llm.txt   /llms.txt   301

Content-Type

text
# _headers
/*.txt
  Content-Type: text/plain; charset=utf-8

/*.json
  Content-Type: application/json; charset=utf-8

Netlify

Static host

Anything in publish/ (or public/ pre-build) is served from the site root.

llm.txt redirect

text
# _redirects
/llm.txt   /llms.txt   301!

Content-Type

text
# _headers
/llms.txt
  Content-Type: text/plain; charset=utf-8

/ai.json
  Content-Type: application/json; charset=utf-8

The trailing ! forces the redirect even when a file exists at the source path — useful if you previously published a literal llm.txt and want to retire it cleanly.

Vercel

Static host

public/ at the project root.

llm.txt redirect

json
// vercel.json
{
  "redirects": [
    { "source": "/llm.txt", "destination": "/llms.txt", "permanent": true }
  ]
}

Content-Type

json
// vercel.json
{
  "headers": [
    {
      "source": "/(.*).txt",
      "headers": [
        { "key": "Content-Type", "value": "text/plain; charset=utf-8" }
      ]
    }
  ]
}

nginx

Web server

Your document root, alongside robots.txt.

llm.txt redirect

nginx
location = /llm.txt {
    return 301 /llms.txt;
}

Content-Type

nginx
location ~ \.txt$ {
    default_type text/plain;
    charset utf-8;
}

location ~ \.json$ {
    default_type application/json;
    charset utf-8;
}

Apache

Web server

Your document root, alongside robots.txt.

llm.txt redirect

apache
# .htaccess
Redirect 301 /llm.txt /llms.txt

Content-Type

apache
# .htaccess
AddType "text/plain; charset=utf-8" .txt
AddType "application/json; charset=utf-8" .json

Astro

Framework

public/ — files there are copied to the site root verbatim and are never processed by the build.

llm.txt redirect

js
// astro.config.mjs
export default defineConfig({
  redirects: {
    '/llm.txt': { status: 301, destination: '/llms.txt' },
  },
});

For a static build the redirect is emitted only if your adapter supports it. On Cloudflare Pages or Netlify, use that host’s _redirects file instead — it is more reliable.

Next.js

Framework

public/ at the project root. Do not create app/llms.txt/route.ts unless you need the content to be dynamic.

llm.txt redirect

js
// next.config.js
module.exports = {
  async redirects() {
    return [
      { source: '/llm.txt', destination: '/llms.txt', permanent: true },
    ];
  },
};

Content-Type

js
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:file*.txt',
        headers: [
          { key: 'Content-Type', value: 'text/plain; charset=utf-8' },
        ],
      },
    ];
  },
};

Shopify

Hosted platform

Shopify does not let you write arbitrary files to the domain root. robots.txt is editable via robots.txt.liquid, but llms.txt is not.

Practical options: serve the files from a subdomain you do control and point to them from robots.txt, or put them behind a reverse proxy in front of the store. A Shopify app or a custom page at /pages/llms is not equivalent — the files must be at the domain root, and a consumer fetching /llms.txt will get your 404 page.

WordPress

Hosted platform

Upload to the web root via SFTP or your host’s file manager.

llm.txt redirect

apache
# .htaccess, above the WordPress rewrite block
Redirect 301 /llm.txt /llms.txt

Upload the files to the web root over SFTP, or use any plugin that lets you serve arbitrary static files from the root. Avoid plugins that render the files as WordPress pages — a consumer fetching /llms.txt needs the raw file, not a themed page.

When your host cannot redirect at all

The specifications prefer /llm.txt to be a 301 to /llms.txt. Where that is impossible, publish a byte-identical copy instead — and treat the two as one file forever after. Two files that drift apart send conflicting identity signals, which is worse than publishing neither.

If you keep a copy, add a build step that fails when the two differ. A single line in CI is enough:

ci
cmp -s public/llms.txt public/llm.txt || { echo "llm.txt has drifted from llms.txt"; exit 1; }

Platform behaviour changes without notice. These snippets reflect each platform’s documented configuration format at the time of writing — verify with curl -I after deploying, which is the only check that actually proves anything.

Something here out of date or wrong for your host? Email [email protected].