Table of contents
- Quick summary
- Two routes, and the honest trade between them
- Route one: drop the file in static/
- Route two: make llms.txt something Hugo builds
- The filename changed in v0.146.0, and Hugo's own docs disagree
- Why baseof.html does not wrap the file
- Verify the build, then verify the live site
- Where AIScan fits, and where it doesn't
- After the deploy
Hugo has no llms.txt setting, no plugin and no checkbox. What it has instead is an output format system that already builds robots.txt and sitemap.xml the same way, which means the file can be generated from your content on every build rather than hand-maintained. Two routes get you a working /llms.txt. One takes thirty seconds and goes stale. The other takes ten minutes and never does.
Quick summary
| Question | Answer for Hugo |
|---|---|
| Is there a built-in llms.txt? | No. Hugo ships robots, sitemap and rss output formats. There is no llms format. |
| Fastest working route | Put a file at static/llms.txt. Hugo copies it to public/llms.txt on build. |
| Route that stays current | Define a custom output format and a layouts/home.llms.txt template that ranges over your pages. |
| Media type to use | text/plain, already registered in Hugo with the txt suffix. No mediaTypes block needed. |
| Template filename | layouts/home.llms.txt on Hugo v0.146.0 and later. Older tutorials say layouts/index.llms.txt. |
| Verify with | npx aiscan-cli yoursite.com, then read row C2. |
| Hugo's own site | gohugo.io/llms.txt returns 404, verified on 3 September 2026. |
Two routes, and the honest trade between them
static/llms.txt | Custom output format | |
|---|---|---|
| Setup time | Under a minute | Five to ten minutes |
| Updates when you publish a post | No | Yes |
| Needs a template | No | Yes, one file |
| Breaks if you rename a section | Silently | Loudly, at build time |
| Good for | Small sites, a hand-curated index | Blogs, docs, anything with more than ~20 pages |
Hugo's directory-structure documentation describes static in its own words as the directory that "contains files that will be copied to the public directory when you build your project", and names robots.txt as one of its examples. So static/llms.txt works, today, with no configuration at all. It is also a file that nobody will remember to edit six months from now.
Route one: drop the file in static/
Create static/llms.txt in your project root:
# Example Site
> Short description of what this site covers.
## Guides
- [Getting started](https://example.com/guides/getting-started/): First-run setup
- [Deploying](https://example.com/guides/deploying/): Build and ship
## Reference
- [Configuration](https://example.com/reference/config/): Every setting
Run hugo and the file lands at public/llms.txt untouched. That is the whole route.
Route two: make llms.txt something Hugo builds
Hugo's output format table already contains the exact shape you want. According to Hugo's configuration documentation, the built-in robots format is mediaType: text/plain, baseName: robots, isPlainText: true. Copy that shape and change the base name.
Add to hugo.toml:
[outputFormats.llms]
mediaType = 'text/plain'
baseName = 'llms'
isPlainText = true
[outputs]
home = ['html', 'rss', 'llms']
Two things worth knowing before you paste that. First, text/plain is already a registered media type in Hugo with txt as its only suffix, fetched from the default media-type table, so you do not need a [mediaTypes] block. Second, keep html first in the home list. Hugo's outputs documentation says the order matters and that "the first element will be the primary output format for that page kind, and in most cases that should be html", which is also what its own worked example does when it adds a third format.
Now the template. Create layouts/home.llms.txt:
# {{ site.Title }}
> {{ site.Params.description }}
## Pages
{{ range .Site.RegularPages }}
- [{{ .LinkTitle }}]({{ .Permalink }}): {{ .Summary | plainify | truncate 120 }}
{{- end }}
Build with hugo and read public/llms.txt. Every new post appears in it on the next build, which is the entire point of choosing this route.
The filename changed in v0.146.0, and Hugo's own docs disagree
This is the single most common way route two fails. Hugo's template lookup page says, verbatim, "We did a complete overhaul of Hugo's template system in v0.146.0", and that page's own examples still use the pre-overhaul names such as index.amp.html. The current pages use the new ones: the RSS templates page shows custom feeds living at layouts/home.rss.xml, layouts/section.rss.xml and layouts/term.rss.xml.
The pattern is <kind>.<output format name>.<suffix>. For a home-page llms.txt built by an output format named llms with a txt suffix, that is layouts/home.llms.txt. A tutorial written before March 2025 will tell you layouts/index.llms.txt, and on a current Hugo you will get a build that produces nothing while reporting no error.
Why baseof.html does not wrap the file
The other silent failure is a /llms.txt that arrives full of your site's HTML shell. Hugo's template-types documentation gives the rule in its own words: a base template is applied only if the template being parsed "must include at least one define action" and "can only contain define actions, whitespace, and template comments". A plain-text template containing raw text and range blocks meets neither condition, so Hugo "executes it exactly as provided, without applying a base template".
If you see <!DOCTYPE html> at the top of your generated file, you have wrapped the body in {{ define "main" }}. Remove it.
Verify the build, then verify the live site
Local check first, because a build failure and a deploy failure look identical from outside:
hugo --cleanDestinationDir
head -5 public/llms.txt
Then check what agents actually receive. Lead with the scan, because it answers the question the file exists to answer:
npx aiscan-cli yoursite.com
Read row C2. C2 passes when the file returns 200, carries an H1, has at least one ## section, and contains markdown links. It does not require a particular content type, which is why Hugo serving text/plain is fine.
By hand, if you would rather:
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' https://yoursite.com/llms.txt
# expect: 200 text/plain; charset=utf-8
Hugo sites are unusually trustworthy under a plain status check. Probing gohugo.io on 3 September 2026 returned a hard 404 for /llms.txt, /robots.txt and an invented page path alike, with /sitemap.xml returning 200 application/xml. Hugo does not answer a missing file with the app shell the way several JavaScript-first hosts do, so a 200 here means a real file. Hugo's own site, incidentally, does not publish an llms.txt at all.
Where AIScan fits, and where it doesn't
| C2 can tell you | C2 cannot tell you |
|---|---|
| The file resolves and returns 200 | Whether the links inside it resolve |
It has an H1 and ## sections | Whether the summaries describe the pages honestly |
| It contains markdown links | Whether a stale static/llms.txt is missing half your posts |
| Which dimension score it moved | Whether your build ran at all |
Our llms.txt generator writes a spec-shaped file you can paste into static/llms.txt as a starting point, and the mistakes that break a valid file covers what goes wrong inside the content itself. If you are weighing whether this is worth the ten minutes at all, the evidence on llms.txt is the honest version.
After the deploy
Rebuild, deploy, then scan the live domain rather than a preview URL. Run npx aiscan-cli against your production host and read C2 in the content dimension. The full rubric for that dimension, including what sits alongside C2, is on the content checks page.
Two neighbouring platforms solve this differently and are worth a look if you run more than one stack: llms.txt on Astro uses a file-extension endpoint rather than an output format, and llms.txt on Framer is a plan-gated upload with no template involved. Every fix guide we publish is indexed at aiscan.site/guides.
Frequently asked questions
My /llms.txt returns 404 after I deployed. What went wrong?
Check the build output before the live site. Run hugo --cleanDestinationDir and look for public/llms.txt. If it is missing, the site never built the file and the deploy is not the problem. The two usual causes are a template filename Hugo does not look for, and an outputs list for the home page kind that does not include your format name.
Hugo builds with no error but public/llms.txt does not exist. Why is there no warning?
An output format with no matching template produces nothing and does not fail the build. Confirm two things: [outputs] home includes 'llms', and the template is at layouts/home.llms.txt. Hugo overhauled its template system in v0.146.0, and a tutorial written before that will tell you layouts/index.llms.txt, which current Hugo does not look for.
My llms.txt starts with <!DOCTYPE html> and contains my whole site header. How do I stop that?
Your template is getting wrapped by layouts/baseof.html. Hugo applies a base template only when the template being parsed contains at least one define action and nothing else besides whitespace and comments. Remove the {{ define "main" }} wrapper from layouts/home.llms.txt and Hugo will execute the template exactly as written, with no HTML shell.
Do I need to add a [mediaTypes] block to hugo.toml?
No. text/plain is already in Hugo's default media type table with txt as its only suffix. You only need a [mediaTypes] entry when you are inventing a type Hugo does not already know, which llms.txt does not require.
Should I use static/llms.txt or a custom output format?
Use static/llms.txt if your site is small and the index is hand-curated, because it takes under a minute and needs no template. Use the output format if you publish regularly, because the file is rebuilt from your actual pages every time you run hugo and cannot silently fall behind your content.
Does the file have to be served as text/markdown?
No. AIScan's C2 check looks for a file that returns 200, carries an H1, has at least one ## section and contains markdown links. It does not require a particular content type. Hugo serving the file as text/plain; charset=utf-8 passes.
Does Hugo generate an llms.txt automatically the way it generates a sitemap?
No. Hugo ships robots, sitemap, sitemapindex and rss output formats, and there is no llms format among them. Hugo's own site is a live example: gohugo.io/llms.txt returned a 404 when it was probed on 3 September 2026.
I put llms.txt under a docs subpath and the scan still says it is missing. Is that a bug?
It is a known limitation on our side, and we have said so publicly. AIScan's C2 check probes /llms.txt at the origin root and stops there, so a valid file at /docs/llms.txt is reported as absent even though the llms.txt spec allows subpath scoping. Until we fix it, publish a file at the origin root as well.
Related guides
The complete AI readiness setup for Squarespace in 2026
Squarespace hands you a finished website and a finished discovery layer at the same time. The robots.txt is written for you, the sitemap is generated for you, and the RSS feed already exists and is…
AI Crawler Traffic in 2026: Only 51.8% of Sites Can Answer "Nothing Changed"
Verified 5 September 2026. All measurements in this article were taken on that date. Every AI crawler that visits your site asks the same handful of questions over and over. Where is your robots.txt.…
How to publish a valid llms.txt on Docusaurus
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 platform's most common deployment target, a project site…
The complete AI readiness setup for Framer in 2026
A brandnew Framer site scores better on an agent readiness scan than a new site on almost any other hosted builder, and that is exactly what makes the remaining gaps hard to see. Framer prerenders…
