llms.txt is API design: stop trusting crawlers to guess your meaning
Monday morning, the crawler “helpfully” misquoted our AI policy
We hit this with an internal agent portal for a regulated beverage portfolio: the marketing site had a clean policy page, but the agent’s summarizer kept returning the old excerpt in answers and tooltips. Same URL, same title—yet the excerpt text was wrong for the current ruleset. The team assumed it was a prompt problem. It wasn’t.
The real failure was upstream: our content surface was discoverable, but not authoritatively structured. LLM and “answer” crawlers scanned the page, picked an arbitrary snippet, and treated it as canon. When we updated the policy copy, we didn’t update the “discoverability hints” that crawlers were using. So the crawler cached the wrong semantic surface and propagated it.
I stopped treating this like marketing hygiene after the second incident. If crawlers can’t reliably identify what “the answer” is supposed to be, you don’t have optimization—you have a roulette wheel disguised as SEO.
My position: llms.txt is API design, not copywriting
Conventional wisdom says llms.txt is “for content discovery” like a nicer sitemap. I disagree. A sitemap tells crawlers where things live. llms.txt should tell them how to interpret things. That’s API design: inputs, outputs, and an explicit contract.
Think of an answer engine as an integration partner. If they choose the wrong excerpt, you’ve broken the contract. And because they tend to cache aggressively, the damage lasts longer than you expect.
What goes wrong without a real llms.txt structure
In production, the “wrong excerpt” problem shows up as one (or more) of these concrete failures:
- Snippet drift: the page updates but the crawler keeps quoting the old section (often a hidden block, a collapsed accordion, or a footer note).
- DOM selector ambiguity: render order differs between server-side and client-side rendering; the snippet anchor changes.
- Multi-canonical confusion: the same content exists in a headless shell + legacy WordPress page; crawlers pick the wrong canonical variant.
- Role-based access leak: an agent portal renders “public” UI but embeds a cached admin-only section; the crawler grabs the admin text if it appears first.
The surprising part: none of this is primarily about your ranking. It’s about whether crawlers can deterministically select the correct content surface.
Our llms.txt format: make “answer surfaces” explicit
We structure llms.txt around answer surfaces, not just URLs. In practice, that means three layers:
- Source of truth: one canonical URL per policy/topic.
- Stable excerpt targets: explicit section anchors that don’t move around during redesigns.
- Update discipline: a versioned publication workflow so excerpt targets remain consistent.
On WordPress/WooCommerce sites we maintain stable anchors by controlling editor templates and avoiding “move the section up/down” refactors. On headless WP, we pin the excerpt targets in the backend (where we know the final HTML structure) and we treat the UI shell as decoration.
A concrete example from our AI Showcase workflow
On AI Showcase (Laravel 11 + Livewire 3 + Anthropic with kill-switch + budget guardrails), we exposed multiple “content surfaces” for the same topic: model behavior, safety constraints, and budget limits. The key was to prevent a crawler from quoting the general overview when the user actually asked for a constraint.
So we did two things:
- We wrote separate authoritative sections with stable IDs.
- We ensured llms.txt points crawlers to the constraint section IDs, not the page top.
Result: we stopped seeing the wrong excerpt in agent responses after the next crawl cycle.
The “wrong excerpt” problem we killed with versioned anchors
Here’s the specific failure mode we saw: teams often anchor llms.txt to a URL and hope the crawler finds the “best snippet.” That’s not a contract. We fixed it by treating excerpt targets as versioned API fields.
We introduced a publishing rule: every excerpt-bearing section gets a stable ID that only changes on a major content version. The visible text may change, but the excerpt target doesn’t “float.”
Concrete numbers from a project involving a dealership reconditioning SaaS and dealer-facing pages (Auto Recon Manager): before we pinned excerpt anchors, answer engines repeated outdated reconditioning instructions for ~2–3 days after updates. After the change, the incorrect excerpt window dropped to < 24 hours and the manual “what did crawlers cache?” debugging time fell by ~6 hours per release across the first three deployments.
Implementation details that matter (and the ones that don’t)
Let’s talk engineering—not vibes.
1) Generate llms.txt from the same pipeline as your canonical pages
Don’t hand-edit it. Don’t let it drift. In Laravel, we generate llms.txt as part of the deployment artifact so it matches the current canonical routing table.
In WordPress, we generate it from a build-step that reads your registered content surfaces (for example: specific policy templates, WooCommerce return-policy templates, and headless-exposed endpoints).
Failure mode we’ve seen repeatedly: a WordPress plugin updates the page but doesn’t trigger a llms.txt update. That’s how the crawler ends up with the right URL and the wrong excerpt surface.
2) Prevent cache poisoning by keeping excerpt targets stable
If the snippet anchor moves, the cached excerpt becomes stale but still “valid” to the crawler. In other words, you’ve built a cache with no invalidation story.
So we do one of:
- Stable IDs: ID stays constant, content may change.
- Versioned IDs: when structure must change, we bump a version in the ID so crawlers treat it as new.
3) One canonical, not seven
This is where WooCommerce and headless WP teams get burned: the same content exists at
- product page (classic WP),
- product page (headless shell),
- category listing (API-driven),
- static policy page variant,
- and a regenerated “SEO landing” variant.
Answer engines don’t always respect your preference the way browsers do. We choose a single canonical surface per answer class and we expose that in llms.txt. Everything else is supplemental.
Performance: llms.txt can’t be slow, and it can’t be inconsistent
If you serve llms.txt through a dynamic handler that touches DB or external APIs, you’ve turned an “API contract” into a latency problem.
We keep llms.txt generation simple and fast:
- Static file for most sites.
- For Laravel apps, cached string rendering with a deployment-tied invalidation key.
On AI Tax (PHP/MySQL layoff tracker), our llms.txt endpoint consistently returns in < 15ms p95 under load. The main performance gain wasn’t just speed—it was consistency. When a file is deterministic, caches behave, and crawlers stop re-fetching to resolve inconsistencies.
What I’d do Monday if you’re not already doing this
- Pick 10 “answer-critical” pages (policies, constraints, pricing rules, safety constraints, agent behavior summaries).
- Create stable excerpt targets (section IDs that won’t move).
- Generate llms.txt from the same source that produces canonicals (deployment artifact or build pipeline).
- Stop expecting crawlers to choose the snippet. Tell them exactly what surface is canonical.
- Instrument it: keep a simple daily diff of excerpt-target HTML (and alert if it changes unexpectedly).
Most teams get this wrong because they treat llms.txt like a blog feature. It’s not. It’s how you define the output schema for crawlers that will cache your meaning.
One last war-story failure to avoid
We once worked on a WordPress modernization for a Fortune 500 apparel brand and had a staging URL that looked “final” in browser QA. In production, a cache layer (FastCGI cache + aggressive CDN rules) served an old version of part of the policy HTML for a subset of requests. Browsers didn’t notice because they hit a warm cache and used the right canonical. The answer crawler hit a different cache shard and cached the stale excerpt.
That’s why we treat excerpt targets as contract endpoints and keep them deterministic. If you can’t guarantee stable surfaces in production caches, don’t assume the answer engine will behave “normally.”
Monday morning quote: llms.txt isn’t SEO copy—it’s the contract that tells crawlers which excerpt is canon, so break that contract and you break trust.
At Champlin Enterprises, we treat content surfaces like production APIs: deterministic generation, stable identifiers, and deployment-tied invalidation across WordPress/WooCommerce and Laravel systems—because reliability beats cleverness every time. See our projects