Table of contents
- TL;DR
- Contents
- What every platform's file has to contain
- Astro
- Astro path A: a static file in public/
- Astro path B: an endpoint built from your content collections
- Astro path C: Starlight documentation sites
- Three Astro traps
- Docusaurus
- Why the file lands at a URL agents will not ask for
- Docusaurus route 1: the static folder
- Docusaurus route 2: a postBuild hook that reads your real routes
- Docusaurus route 3: an npm plugin
- Choosing which Docusaurus pages to list
- Confirm which Docusaurus URL you shipped
- Framer
- The Framer plan gate comes first
- Upload the file in Site Settings
- Point the Framer links at Markdown twins
- Verify the Framer file landed
- Ghost
- Why Ghost gives you nowhere to put the file
- Add the Ghost route
- Write the Ghost template
- Self-hosted Ghost shortcut
- Read the Ghost response, not the status code
- Confirm any platform's file passes C2
Updated on 24 October 2026. Sections for Astro, Docusaurus, Framer and Ghost were checked on the dates shown inside each one. The other seven platforms are being folded into this guide and keep their own posts until they arrive.
A valid llms.txt is one file at /llms.txt that returns HTTP 200 and contains an H1, at least one ## section and Markdown links. That is the whole of AIScan's C2 check. What changes from platform to platform is not the file but where you are allowed to put it: a folder, a code endpoint, a paid upload panel or a template route. This guide holds the platform-specific steps in one place, so you can jump to your stack.
TL;DR
- The pass bar is the same everywhere. C2 wants a 200, one H1, one
##section and Markdown links. It does not check the content type. - Astro: drop
public/llms.txt, or buildsrc/pages/llms.txt.tsfrom your content collections. Starlight sites can use thestarlight-llms-txtplugin. - Docusaurus:
static/llms.txtis served under yourbaseUrl. On a GitHub Pages project site that means/project/llms.txt, and the origin root returns 404. - Framer: an upload under Site Settings, Hosting, Files, on the Pro or Enterprise plan only. Use a
.txtextension and the root path. - Ghost: no setting and no file root. Add a
routes.yamltemplate route withcontent_type: text/plain, and judge success by the final URL, not the status code. - Google does not use the file. Publish it for the agents and tools that fetch it. Whether any do is a separate question, measured in does llms.txt actually work in 2026.
Contents
| Platform | Where the file comes from | Section |
|---|---|---|
| Astro | static file, build-time endpoint, or Starlight plugin | Astro |
| Docusaurus | static/ folder, a postBuild hook, or an npm plugin | Docusaurus |
| Framer | a Pro-plan file upload | Framer |
| Ghost | a routes.yaml template route | Ghost |
| Hugo | still its own guide: llms.txt on Hugo | coming |
| Next.js | still its own guide: llms.txt on Next.js | coming |
| Replit | still its own guide: llms.txt on Replit | coming |
| Shopify | still its own guide: llms.txt on Shopify | coming |
| Squarespace | still its own guide: llms.txt on Squarespace | coming |
| Webflow | still its own guide: llms.txt on Webflow | coming |
| Wix | still its own guide: llms.txt on Wix | coming |
What every platform's file has to contain
The llms.txt spec was proposed by Jeremy Howard of Answer.AI on 3 September 2024. An H1 carrying the project name is the only required section. Version 2, published 10 August 2026, added rel="alternate" and rel="describedby" link relations for discovery. No standards body owns it, and Google's AI optimization guide says Google Search ignores these files. The page was re-fetched on 24 October 2026 and still answers 200.
A minimum passing file:
# Example
> Guides and API reference for Example.
## Docs
- [Quickstart](https://example.com/docs/quickstart): install and first request
- [API reference](https://example.com/docs/api): every endpoint, with examples
If you would rather not hand-roll it, the llms.txt generator drafts one from a URL. The mistakes that break a file from the inside are catalogued in llms.txt validator: the common mistakes, and how it differs from your other root files is in llms.txt vs robots.txt vs sitemap.
How the platforms differ, from the guides folded in so far:
| Platform | Cost of a valid file | Stays current on its own | Plan gate | Checked |
|---|---|---|---|---|
| Astro | 2 minutes (static) or 15 minutes (endpoint) | Yes, with the endpoint | None | 28 August 2026 |
| Docusaurus | 5 minutes (static), 20 minutes (hook) | Yes, with a hook or plugin | None | 4 September 2026 |
| Framer | about 10 minutes | No | Pro or Enterprise | 2 September 2026 |
| Ghost | about 15 minutes | Yes, the template reads your posts | None | 30 August 2026 |
Astro
Verified 28 August 2026. Plugin version re-checked 24 October 2026.
A valid llms.txt on Astro is either a two-minute file drop or a twenty-line endpoint that rebuilds itself from your content collections every time you run astro build. Astro's own documentation site does not publish one (checked 28 August 2026), so there is no upstream example to copy.
| If you want to | Do this | Where it lives | Time |
|---|---|---|---|
| Ship something valid today | Hand-write the file as a static asset | public/llms.txt | 2 minutes |
| Keep it current without editing it | Generate it from getCollection() | src/pages/llms.txt.ts | 15 minutes |
| Cover a Starlight docs site | Install starlight-llms-txt | astro.config.mjs | 5 minutes |
The endpoint is the version that survives a real blog. A hand-written file is stale the day you publish your next post.
Astro path A: a static file in public/
Astro's project structure guide describes public/ as the home for "non-code, unprocessed assets", and its own example tree puts robots.txt there. Everything in public/ is copied to the build root untouched.
- Create
public/llms.txtat the top level of your project, besidesrc/andastro.config.mjs. - Paste the Markdown above and swap in real absolute URLs.
- Run
npx astro buildand confirm the file appears atdist/llms.txt. - Deploy.
That is a correct answer for a marketing site with eight pages. On anything with a content collection behind it, you will forget to update it.
Astro path B: an endpoint built from your content collections
Astro's endpoints guide says a .js or .ts file in src/pages becomes a route with the extension stripped, so src/pages/data.json.ts builds /data.json. The same rule gives you /llms.txt from src/pages/llms.txt.ts. In a statically generated site, custom endpoints "are called at build time to produce static files", so this costs nothing at runtime.
Set site in astro.config.mjs first, because the spec wants absolute URLs:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({ site: 'https://example.com' });
Then create the endpoint:
// src/pages/llms.txt.ts
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';
export const GET: APIRoute = async ({ site }) => {
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => +b.data.pubDate - +a.data.pubDate);
const body = [
'# Example',
'',
'> Guides, API reference and engineering notes.',
'',
'## Blog',
'',
...posts.map(
(p) => `- [${p.data.title}](${new URL(`/blog/${p.id}/`, site)}): ${p.data.description}`,
),
'',
].join('\n');
return new Response(body, {
headers: { 'content-type': 'text/plain; charset=utf-8' },
});
};
getCollection() takes an optional filter function, which is how drafts stay out. Your src/content.config.ts schema already guarantees that title and description exist on every entry, and that is what makes the generated file reliable rather than hopeful.
Astro path C: Starlight documentation sites
If your docs run on Starlight, starlight-llms-txt does the whole job. It is written by Chris Swithinbank, a Starlight maintainer. Version 0.11.0 was published on 1 July 2026, and npm listed 0.12.0 as the latest on 24 October 2026. Install it with npm i starlight-llms-txt, add starlightLlmsTxt() to the plugins array inside starlight() in astro.config.mjs, and set site. It generates llms.txt, llms-full.txt and llms-small.txt, and the plugin docs tell you to preview at localhost:4321/llms.txt.
Three Astro traps
A trailing slash returns 404. Astro's endpoints guide is explicit: routes whose URLs include a file extension "can only be accessed without a trailing slash", whatever your build.trailingSlash setting says. So /llms.txt/ fails while /llms.txt works. Always test the bare path.
On-demand rendering regenerates the file on every request. If you run an adapter with server output, add export const prerender = true; to the endpoint so it is written once at build time.
Do not chase the content type. C2 does not check it. Both nextjs.org/docs/llms.txt and vercel.com/llms.txt serve text/plain; charset=utf-8, and both pass.
Docusaurus
Verified 4 September 2026. Package versions re-checked 24 October 2026.
A Docusaurus site can publish a completely valid llms.txt and still score zero on it. The reason is one config value. On GitHub Pages, the most common deployment target, a project site lives at https://your-org.github.io/your-project/, so baseUrl is /your-project/ and the file you dropped into static/ answers at /your-project/llms.txt. Nothing answers at the origin root. The build was fine. The URL was wrong.
| If you want to… | Do this | Time | Stays current on its own |
|---|---|---|---|
| Ship a file today | Put llms.txt in static/ | 5 min | No, you edit it by hand |
| Generate it from your real routes | Add an inline plugin with a postBuild hook | 20 min | Yes, every build |
| Skip writing code | Install a published plugin from npm | 10 min | Yes, every build |
| Serve it at the origin root | Deploy at baseUrl: '/', or add a host-level rewrite | Varies | N/A |
Why the file lands at a URL agents will not ask for
Docusaurus copies static/ straight into the build output. Its Static Assets page says: "Every file you put into that directory will be copied into the root of the generated build folder with the directory hierarchy preserved." The next paragraph is the one that matters: "for site baseUrl: '/subpath/', the image /static/img/docusaurus.png will be served at /subpath/img/docusaurus.png." According to the deployment guide, "For a site deployed at https://my-org.com/my-project/, baseUrl is /my-project/."
| Deployment | baseUrl | static/llms.txt is served at | Origin root /llms.txt |
|---|---|---|---|
docs.example.com (own domain) | / | https://docs.example.com/llms.txt | 200 |
example.com/docs/ (subpath) | /docs/ | https://example.com/docs/llms.txt | 404 |
org.github.io/project/ (GitHub Pages project site) | /project/ | https://org.github.io/project/llms.txt | 404 |
A subpath file is not invalid. The spec, fetched from llmstxt.org on 4 September 2026, allows it: "The llms.txt file spec is for files named llms.txt, at the root path /llms.txt of a website or at any subpath (e.g. /docs/llms.txt). A file covers the URLs under its path, and where more than one file applies, agents should use the most specific one." So /docs/llms.txt is spec-legal and useful. It is also invisible to any scanner that probes only the origin root, ours included. Know which one you published before you go looking for the score.
Docusaurus route 1: the static folder
- Create
static/llms.txtat the root of your Docusaurus project, besidedocusaurus.config.js. - Write the file, listing pages that answer questions (see below).
- Run
npm run build, thennpm run serve, and openhttp://localhost:3000plus yourbaseUrlandllms.txt. - Deploy.
If your team keeps generated assets somewhere other than static/, the staticDirectories option in docusaurus.config.js takes an array of paths, all copied to the build output as-is.
Docusaurus route 2: a postBuild hook that reads your real routes
Hand-written indexes drift the moment somebody adds a page. According to the lifecycle API reference, postBuild(props) is "Called when a (production) build finishes" and receives siteDir, outDir, baseUrl, siteConfig, routesPaths and routesBuildMetadata, which carries a noIndex flag per location. Add this to docusaurus.config.js as an inline plugin:
plugins: [
function llmsTxtPlugin() {
return {
name: 'llms-txt',
async postBuild({siteConfig, routesPaths, routesBuildMetadata, outDir}) {
const fs = await import('node:fs/promises');
const path = await import('node:path');
const lines = [
`# ${siteConfig.title}`,
'',
`> ${siteConfig.tagline}`,
'',
'## Docs',
];
for (const route of routesPaths) {
if (routesBuildMetadata?.[route]?.noIndex) continue;
if (route === '/404.html') continue;
lines.push(`- [${route}](${siteConfig.url}${route})`);
}
await fs.writeFile(
path.join(outDir, 'llms.txt'),
lines.join('\n') + '\n',
);
},
};
},
],
Writing into outDir puts the file at the build root, the same place static/ lands, so the baseUrl rule above still applies. Before you ship it, replace the raw route path with a real title, and group routes under separate ## headings rather than one flat list. Agents read the headings.
Docusaurus route 3: an npm plugin
| Package | Version | What it produces |
|---|---|---|
docusaurus-plugin-llms | 0.6.0 on 4 September 2026; 0.6.1 on 24 October 2026 | An llms.txt index following the llmstxt.org format |
@signalwire/docusaurus-plugin-llms-txt | 1.2.2 on both dates | Markdown twins of your HTML pages, plus an llms.txt index |
The second solves a problem the first does not touch: your links can point at Markdown rather than HTML, which is what an agent wants at the other end. Both are community packages, not part of @docusaurus/core. Read the README and pin the version.
Choosing which Docusaurus pages to list
A docs site's route list includes tag pages, archive pages and pagination, and none of those help a model. Start with the H1 naming the project, follow it with a blockquote summary, group links under ## headings such as ## Getting started and ## API reference, give every link a short note, and leave out anything carrying noIndex.
Confirm which Docusaurus URL you shipped
curl -sI https://yoursite.com/llms.txt | head -1
curl -sI https://yoursite.com/your-baseurl/llms.txt | head -1
Expect HTTP/2 200 from whichever one matches your baseUrl. A 404 from the origin root while the subpath returns 200 is the failure this section exists for. Status codes can be trusted on Docusaurus: verified on 4 September 2026, docusaurus.io/llms.txt returns a real 404 with a text/html body of 20,038 bytes, and an invented path returns the identical 404. Then read the body with curl -s https://yoursite.com/llms.txt | head -20. The first non-blank line must be a single # heading. If you see HTML, you are looking at your 404 page through a redirect.
AIScan's C2 check probes /llms.txt at the origin of the URL you give it. Give it the subpath URL and it still asks the origin root, so a spec-legal /docs/llms.txt reads as absent. That is a limit of the check rather than a fault in your file.
Framer
Verified 2 September 2026. Framer's help articles are script-rendered and were not re-fetched on 24 October 2026, so the plan and path details below carry their September date.
On Framer, llms.txt is not a file you commit and not a text box you fill in. It is an upload in a hosting panel, and that panel is switched off below a certain price.
| Question | Answer |
|---|---|
| Where does the file live? | Uploaded under Site Settings → Hosting → Files, with the path set to the domain root |
| Which plans can do it? | Pro and Enterprise only. Free and Basic sites cannot host a static file at all |
| What formats are accepted for llms.txt? | TXT for a standard file, JSON for structured data |
| Does it work before publishing? | No. The file appears only after the site is published |
| Does Framer generate it for you? | No. Framer auto-generates robots.txt and sitemap.xml, not llms.txt |
| Time to fix | About ten minutes, once you are on a plan that allows it |
The Framer plan gate comes first
Framer's static files article says, in its own words, "This feature is available on Pro and Enterprise plans." There is no workaround.
| Source | What it says about plans | Last updated |
|---|---|---|
| Framer help, "Static files" | "available on Pro and Enterprise plans" | 10 August 2026 |
| Framer help, "Add an llms.txt file to your site" | Pro and Enterprise in the body, and again in its FAQ | 10 August 2026 |
Framer pricing, snapshot in Framer's own llms.txt | Pro is $30 per month, billed monthly | 30 August 2026 |
According to the same article, "These settings used to live in the Domains dashboard and are now in your project's Site Settings, under Hosting." Older tutorials still point at the Domains dashboard, so ignore them.
Upload the file in Site Settings
- Open your dashboard.
- Open Site Settings.
- Go to Hosting → Files.
- Click the Add button.
- Upload your file.
- Set the file path. Framer's step says "do not include the file name", and its worked example is a path of
/pdfsproducingexample.com/pdfs/example.pdf. Its FAQ answers the root case directly: "You can upload a file and assign it to a root-level path such as/ads.txtor/llms.txt." So upload a file namedllms.txtand give it the root path. - Publish the site.
Delivery depends on the MIME type. text/plain, text/xml, application/json, application/pdf and the image, audio and video families render in a browser, and in Framer's phrasing "all other file types will automatically download instead of rendering." Upload Markdown with an .md extension and a crawler gets a download prompt rather than a body. Use .txt. The same panel handles .well-known files: set the path to /.well-known for security.txt, assetlinks.json or an AI plugin manifest.
Point the Framer links at Markdown twins
Framer serves a Markdown version of every optimised page at the same URL, through an Accept: text/markdown header or by appending ?md. Measured on 2 September 2026, www.framer.com/pricing with that header returned 200, text/markdown; charset=utf-8, 11,651 bytes, opening with YAML front matter. Framer's own file, fetched from www.framer.com/llms.txt that day, was 5,900 bytes and points at https://www.framer.com/ai/?md and its siblings rather than the HTML pages. It still answered 200 on 24 October 2026.
The caveat comes from Framer's AI agents article: "Markdown is only generated for optimized pages." That is not theoretical. Framer's own help articles are not optimised, and Accept: text/markdown on /help/articles/static-files/ returns text/html. Test any URL before you put its ?md form in your index.
Verify the Framer file landed
| What you see | What it means |
|---|---|
200 and text/plain | Working. Framer serves it from the edge with server: Framer |
404, tiny body | The upload is missing, the path is wrong, or the site has not been published since |
| A download prompt in the browser | Wrong extension. Re-upload as .txt |
200 but empty content | The file uploaded is empty; C2 will still fail |
Framer returns a real 404 for a missing root file. Verified on 2 September 2026, www.framer.com/llms-full.txt returned 404 with a ten-byte body, while an unknown page path returned the site's own 404 page. Framer does not answer 200 with the homepage the way Ghost does, so a status check on Framer is trustworthy.
Ghost
Verified 30 August 2026. The routing page was re-fetched on 24 October 2026 and still documents content_type.
Ghost has no setting for llms.txt and no folder you can drop a file into that lands on the site root. The fix takes about fifteen minutes: one route in routes.yaml, one Handlebars template, one upload. Ghost's routing documentation says routes render HTML unless you override it "by specifying a content_type property with a custom mime-type". That property is the whole trick.
| If you are on… | Do this | Where the file ends up | Time |
|---|---|---|---|
| Ghost(Pro) | Add a template route in routes.yaml, upload it under Settings > Labs | /llms.txt/ (with the slash) | ~15 min |
| Self-hosted Ghost | Same route, or an alias in nginx or Caddy | /llms.txt/, or /llms.txt via the proxy | ~10 min |
| Any Ghost site | Check the final URL and content type, not the status code | n/a | 1 min |
Why Ghost gives you nowhere to put the file
Ghost serves theme files under /assets/ and uploaded media under /content/images/. Neither is the site root, and Ghost admin has no file manager. So on Ghost the file cannot be a file. It has to be a route that renders text, from your posts, every time an agent asks.
Add the Ghost route
Ghost keeps its routing config at content/settings/routes.yaml:
routes:
/llms.txt/:
template: llms-txt
content_type: text/plain
collections:
/:
permalink: /{slug}/
template: index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/
Keep the collections and taxonomies blocks. Uploading a routes file that omits them replaces your whole URL structure. The trailing slash is not optional. According to the routing page, "Trailing slashes are required" for dynamic routing, and Ghost "automatically forces trailing slashes". Writing the route as /llms.txt/ is what makes it resolve.
Write the Ghost template
Create llms-txt.hbs in your theme root, alongside index.hbs. The {{#get}} helper queries the Content API server-side before the template renders:
# {{@site.title}}
> {{@site.description}}
## Posts
{{#get "posts" limit="all" order="published_at desc"}}{{#foreach posts}}- [{{title}}]({{url absolute="true"}}): {{excerpt words="20"}}
{{/foreach}}{{/get}}
## Start here
- [About]({{@site.url}}/about/): who publishes this
The {{#get}} helper reads posts, tags, authors, tiers and newsletters. Pages are not one of those resources, so list your important pages by hand in a second ## section, as above. Zip the theme and upload it in Ghost admin. The uploader runs GScan, and according to Ghost's theme docs "any fatal errors will prevent the theme from being used", so a broken Handlebars block is caught before it reaches the site. Then upload routes.yaml under Settings > Labs. Editing the file on a self-hosted box works too, but Ghost has to be restarted.
Self-hosted Ghost shortcut
Behind nginx or Caddy you can skip the template and serve a static file at the bare path, which routes.yaml cannot give you:
location = /llms.txt {
alias /var/www/llms.txt;
default_type text/plain;
}
The trade-off: the proxy version never updates itself, while the template version regenerates from your posts on every request.
Read the Ghost response, not the status code
Verified on 30 August 2026, we requested /llms.txt from two Ghost publications that do not publish one, Platformer and 404 Media. Both returned 302 to the homepage, which then returned 200 with text/html. A follow-redirects status check therefore reports 200 on a Ghost site with no llms.txt at all. Check the destination:
curl -sIL https://example.com/llms.txt | grep -iE '^(HTTP|location|content-type)'
You want the chain to end on your own /llms.txt/ with content-type: text/plain. If the final location is your homepage, the route is not live.
| What you saw | What it means | Fix |
|---|---|---|
| 302 to the homepage | The route never loaded | Re-upload routes.yaml; confirm the key reads /llms.txt/ with both slashes |
Renders, but text/html | content_type missing or misindented | YAML nesting is two spaces, and Ghost is strict about it |
Renders as text, nothing under ## Posts | Wrong template filename | routes.yaml drops the .hbs, so template: llms-txt needs llms-txt.hbs |
| 200 with content, C2 still fails | A Ghost page slug collides with the route | Routes and slugs know nothing about each other, so one wins. Rename the page |
Re-run the scan after your next theme change, because a theme upload replaces llms-txt.hbs.
Confirm any platform's file passes C2
Scan first. npx aiscan-cli yoursite.com returns the whole content dimension, and row C2 names which of the four conditions failed rather than telling you something is wrong. It is free and needs no account. To check by hand:
curl -sSL -o llms.txt -w '%{http_code}\n' https://example.com/llms.txt # expect 200
head -1 llms.txt # expect "# Example"
grep -c '^## ' llms.txt # expect >= 1
grep -cF '](http' llms.txt # expect >= 1
| C2 can tell you | C2 cannot tell you |
|---|---|
| The file exists and returns 200 | Whether the URLs inside it resolve |
It has an H1 and a ## section | Whether the descriptions are accurate |
| It carries Markdown links | Whether any assistant has ever fetched it |
Server logs answer the last one. Filter for requests to /llms.txt and see who turns up. Read C2 next to C1 (Markdown for agents) and C3 (structured HTML), because the same build step usually moves all three. Every other fix guide sits at aiscan.site/guides.
Frequently asked questions
What does a valid llms.txt need to pass AIScan check C2?
Four things: the file returns HTTP 200, it has an H1, it has at least one ## section, and it contains Markdown links. C2 does not check the content type, so text/plain is fine. A file that loads in the browser but opens with a paragraph instead of a # heading still fails.
Does Google use llms.txt?
No. Google's AI optimization guide states that Google Search ignores these files and that keeping one neither helps nor harms rankings. Publish it for the agents and directed tools that do fetch it.
Where does llms.txt go in an Astro project?
A hand-written file belongs at public/llms.txt, which Astro copies to the build root untouched. A generated one belongs at src/pages/llms.txt.ts, because Astro strips the .ts extension and serves the route as /llms.txt. Test the bare path, since routes with a file extension cannot be reached with a trailing slash.
Why does my Docusaurus llms.txt return 404 at the site root?
Almost certainly baseUrl. Docusaurus copies static/ to the build root, and the build root is served under your baseUrl. If baseUrl is /my-project/, the file answers at /my-project/llms.txt and the origin root has nothing. Deploy at baseUrl: '/' on its own domain or add a host-level rewrite.
Is a file at /docs/llms.txt valid?
Yes. The llms.txt specification allows the file at the root path or at any subpath, and says agents should use the most specific file that applies. Scanners that probe only the origin root, AIScan's C2 included, will report it as absent.
Which Framer plans can host an llms.txt file?
Pro and Enterprise. Framer's static files article says the feature is available on those plans, so Free and Basic sites cannot host a file at the domain root. Upload it under Site Settings, Hosting, Files with the root path, as a .txt file, and publish the site. This was checked on 2 September 2026.
Why does my Framer llms.txt download instead of displaying?
Framer renders only certain MIME types in the browser, including text/plain, and downloads everything else. A file uploaded with an .md extension falls outside that list. Re-upload the same content as llms.txt with a .txt extension.
Does Ghost have a built-in llms.txt setting?
No. As of 30 August 2026 Ghost admin has no llms.txt option and no file manager, so the file has to come from a template route in routes.yaml with content_type: text/plain, or from the reverse proxy in front of a self-hosted install.
Why does a status check say my Ghost site has llms.txt when it does not?
Ghost answers a missing /llms.txt with a 302 to the homepage, which returns 200 with text/html. A check that follows redirects reports 200. Run curl -sIL and confirm the chain ends on your own /llms.txt/ with content-type: text/plain.

About the author
M Asif Rahman
Entrepreneur, curious about AI
I have been building companies for a long time, and lately most of my curiosity goes to AI. AIScan is a small personal project I built on a flight from Dubai to Narita. I use it to understand how ready the web is for AI agents, and I share what the scans teach me.
Related guides
AI readiness setup guide by platform (2026)
Verified 17, 18, 20 and 22 October 2026. Updated on 22 October 2026. An AIready site needs six things: a robots.txt that names AI crawlers D1, B2, a sitemap D2, a declared feed E5, an llms.txt C2,…
Platform League Table: WordPress, Next.js, Astro and Shopify Across 728 Sites
Every few weeks someone asks which platform produces the most agentready sites. WordPress, Next.js, Astro and Shopify all have fans, and a ranked table is the answer people expect. We have the data…
Monitor AI Readiness Over Time and Catch Silent Regressions
Discovery files rot without anyone touching them. A deploy overwrites robots.txt, a plugin update regenerates llms.txt, a CDN rule starts answering every unknown path with 200. Nothing errors, the…
