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
The State of AI Agent Readiness in 2026: 473 Sites Measured
Half of the web's agentreadiness problem is already solved, and almost nobody has noticed which half. Across 473 real websites scanned by AIScan between 24 August and 3 September 2026, the median…
How to publish a valid llms.txt on Framer
Verified on 2 September 2026. Every path, plan limit and status code below was either read from Framer's own help centre or measured live against www.framer.com on that date. On Framer, llms.txt is…
The complete AI readiness setup for Replit in 2026
Verified 2 September 2026. Every command, config key and file path below was run against live Replit apps or fetched from Replit's own documentation on that date. On most platforms a missing…
The complete AI readiness setup for Lovable in 2026
Two Lovable apps can look identical in a browser and differ by a factor of fifty in what an AI crawler actually receives. On 1 September 2026 we fetched fifteen published Lovable apps twice each,…
