Table of contents
Verified 1 September 2026.
On Replit, publishing /llms.txt is not one job. It is two, and which one you have depends on how the app is published. An app built by Agent runs on an Autoscale Deployment with your own server answering every path, so the file is a build asset your server hands out. A Static Deployment has no server at all, so the file is a plain file in a public directory and you can set its content type from .replit. Getting this backwards is why the file looks published and then does not answer.
Either route takes about five minutes. Read the verification step twice, because the app scaffold Agent writes answers 200 for a file that is not there.
Quick summary
| Question | Answer |
|---|---|
| Where does the file go? | client/public/llms.txt on an Agent app (Autoscale); the published public directory on a Static Deployment |
| Does a config change help? | Only on Static, where [[deployment.responseHeaders]] in .replit sets the content type |
| Does it work on the dev URL? | Yes in the workspace preview, but grade the published domain |
| Time to fix | About 5 minutes, plus a republish |
| AIScan check | C2, llms.txt, in the content dimension |
| The trap | The default Express scaffold returns 200 and HTML for any missing path, so a status-code check passes on an app with no file |
Find out which deployment type you have
Open .replit in the app root. According to Replit's configuration reference, these files "are hidden by default. Show them in your Replit App by selecting Show hidden files from the filetree menu." The value to read is deploymentTarget under [deployment].
An app scaffolded by Agent, fetched from the workspace on 1 September 2026, carries this:
[deployment]
deploymentTarget = "autoscale"
build = ["npm", "run", "build"]
run = ["npm", "run", "start"]
That is path A. If the Publishing tool shows a Deployment type of Static, that is path B. The two do not overlap, and Replit says so in its own words on the deployment types page: "Static Deployments are not compatible with Replit Apps created using Agent."
| Deployment type | Who serves /llms.txt | Where the file lives |
|---|---|---|
| Autoscale (default, and every Agent app) | Your Express server | client/public/llms.txt, built into dist/public |
| Static | Replit's cached file server | The public directory you name when publishing |
| Reserved VM | Your server, always on | Same as Autoscale |
| Scheduled | Nothing is served | Not applicable |
Path A: an Agent app on Autoscale
The React and Express scaffold Agent writes puts the front end under client/, and its vite.config.ts sets root to that folder with build.outDir pointing at dist/public. Vite copies the contents of the root's public/ folder into the build output untouched, so a file you drop in client/public/ arrives at the web root.
- In the file tree, open client then public.
- Create a file named
llms.txtthere. Notclient/llms.txt, and not the repository root. - Write the file (the section below covers what belongs inside).
- Click Publish, then Republish. The build step runs
npm run build, which is what copies the file.
The server half is already done for you. The scaffold's server/vite.ts mounts express.static() on the build directory in production, so no route code is needed for a static file.
Path B: a Static Deployment
Here the public directory and an optional build command are set in the Publishing tool under Adjust settings. Put llms.txt at the top level of that directory, republish, and it answers at the root.
Static gives you one thing the other types do not: a content type you control. Add this to .replit in the app root:
[[deployment.responseHeaders]]
path = "/llms.txt"
name = "Content-Type"
value = "text/markdown; charset=utf-8"
According to Replit's static deployment guide, fifteen headers are reserved and cannot be set this way: Accept-Ranges, Age, Allow, Alt-Svc, Connection, Content-Encoding, Content-Length, Content-Range, Date, Location, Server, Set-Cookie, Trailer, Transfer-Encoding and Upgrade. Content-Type is not among them, so this rule takes effect after you republish. Most platforms in this series cannot do that at all, and neither of Replit's own files uses it.
The 200 that means nothing
The scaffold's production server, read from its server/vite.ts and verified on 1 September 2026, ends with a catch-all that sends the app shell for anything it did not match:
app.use(express.static(distPath));
app.use("*", (_req, res) => {
res.sendFile(path.resolve(distPath, "index.html"));
});
A request for /llms.txt on an app with no such file therefore returns HTTP 200 with text/html and your homepage in the body. A checker that reads the status code alone will report success. So will one that follows redirects and stops there, which is the same class of false pass that Ghost produces by a different mechanism, covered in the Ghost guide.
Check the content type and the first line, never the status code by itself.
What Replit's own two files show
The format is small: an H1 with the site name, an optional blockquote summary, then ## sections holding markdown links. Replit publishes two of these and they are worth reading, though not copying wholesale. Both fetched and verified on 1 September 2026:
| File | Bytes | H1 | H2 | H3 | Links ending .md | Content type |
|---|---|---|---|---|---|---|
docs.replit.com/llms.txt | 39,522 | 1 | 9 | 40 | 204 | text/plain; charset=utf-8 |
replit.com/llms.txt | 20,191 | 1 | 16 | 0 | 228 | text/plain |
Two things to take from that. The docs file carries forty ### headings, which is deeper nesting than the format asks for and a common way a generated file drifts, alongside the other mistakes covered in the llms.txt validator guide. And both point overwhelmingly at .md targets rather than HTML pages, which is the part most hand-written files miss. If you would rather not draft one, the llms.txt generator writes a spec-shaped file you can paste in.
Confirm it with a scan, then by hand
Run the scan against the published domain:
npx aiscan-cli yourapp.replit.app
Read row C2, llms.txt, in the content dimension. It reports whether the file exists, returns 200, and contains an H1, at least one ## section and markdown links. AIScan grades it free and without an account, and the same pass covers C1, C3 and E3 on the same page.
If you would rather check by hand, ask for headers rather than a status code:
curl -sI https://yourapp.replit.app/llms.txt | head -3
curl -s https://yourapp.replit.app/llms.txt | head -1
The first should not say text/html. The second should be your # heading, not <!DOCTYPE html>. On the workspace preview URL the same commands work, but grade the published domain, since that is what agents fetch.
Where AIScan fits, and where it doesn't
| C2 can tell you | C2 cannot tell you |
|---|---|
| The file answers at the root of the domain you scanned | Whether the URLs inside it resolve |
It has an H1, a ## section and markdown links | Whether the summaries describe the pages honestly |
| It came back as 200 rather than a redirect | Whether your Static content-type rule survived the republish |
| The file is reachable without JavaScript | Whether the file matches the app you shipped today |
A generated file that lists routes you deleted still passes C2. Re-read it when the app changes.
Publish, then read row C2
Add the file, republish, and scan the published domain. If C2 still fails, the file is in the repository root instead of client/public, or the build did not run. The same scan gives you C1, C3 and E3 on the same page, and the Astro guide covers the build-time version of this same job if you also ship a static site. Every fix guide in this series lives at aiscan.site/guides, the check itself is documented under content checks, and the Replit platform page collects what else we grade on this stack.
Frequently asked questions
My llms.txt returns 200 but the body is my homepage. What went wrong?
The file is not there. The Express scaffold Replit's Agent writes ends with a catch-all that sends index.html for any path it did not match, so a missing file answers 200 with text/html. Check the content type and the first line of the body rather than the status code. If the first line is <!DOCTYPE html>, the file never made it into the build.
I put llms.txt in the repository root and it still 404s or serves the app shell. Why?
On an Agent-built app the web root is not the repository root. The Vite config sets its root to client/ and builds into dist/public, so only files inside client/public are copied to the served output. Move the file to client/public/llms.txt and republish.
It works in the workspace preview but not on the published app.
The preview runs the dev server, which serves client/public directly without a build. The published app serves dist/public, which is only refreshed when the build command runs. Click Publish, then Republish, so npm run build copies the file across.
My scan still fails C2 after republishing. What should I check next?
Three things, in order. Confirm the file is at client/public/llms.txt and not client/llms.txt. Confirm the deployment build command actually ran. Then fetch the published domain rather than the preview URL, because that is the host the check grades.
Where exactly does the file go on a Replit app built by Agent?
client/public/llms.txt. Agent apps publish as Autoscale Deployments, the front end lives under client/, and Vite copies that folder's public directory verbatim into the build output that Express serves.
Can I use a Static Deployment for an app Agent built?
No. Replit's deployment types page states that Static Deployments are not compatible with Replit Apps created using Agent, because Agent builds full-stack apps that need a backend server. Use Autoscale or Reserved VM for those, and Static only for a site with no server.
Does llms.txt have to be served as text/markdown?
No. AIScan's C2 check does not require a content type, and neither of Replit's own two llms.txt files uses text/markdown. On a Static Deployment you can set one anyway with a deployment.responseHeaders entry in .replit, since Content-Type is not on Replit's reserved header list.
Do I need to write the file by hand?
No. The format is an H1, an optional summary blockquote, then ## sections of markdown links. The llms.txt generator at aiscan.site produces a spec-shaped file you can paste into client/public/llms.txt, which is faster than drafting one and avoids the deep heading nesting that generated files often pick up.
Related guides
Docs Sites and AI Agents: Four Markdown Routes, 52 Sites Tested
Ask an AI assistant how to configure a webhook, add a database index, or set a cache header, and it does not go looking for a blog post. It goes to the vendor's documentation. Docs are the…
Gate AI readiness in CI so a regression fails the build
A green pipeline is supposed to mean the site is fine. On an AI readiness check it often means something narrower: that the gate could not tell a file from a phantom. This step, which appears in a…
The complete AI readiness setup for Wix in 2026
Every other platform in this series asks you to create something. Wix has already created it. Before you open a single panel, a Wix site is serving a robots.txt, a sitemap index, serverrendered HTML…
The complete AI readiness setup for Docusaurus in 2026
Docusaurus is the framework most likely to be sitting between an AI agent and the answer it is looking for. It runs a very large share of the developer documentation on the web, and developer…
