llms.txt for Express.js
Express is the most popular Node.js web framework. Serving llms.txt is trivial — either via express.static() for a static file or a dedicated GET route for dynamic generation.
Last updated:
Option 1: static file with express.static()
If your app already uses express.static() to serve a public/
folder, simply drop llms.txt into that folder. Express will serve it at /llms.txt with no additional code.
// server.js
const express = require('express');
const app = express();
// Serve everything in ./public/ at the root URL.
// If public/llms.txt exists, it is available at /llms.txt automatically.
app.use(express.static('public'));
app.listen(3000, () => console.log('Listening on http://localhost:3000'));
// Project structure:
// your-express-app/
// ├── public/
// │ └── llms.txt ← add this
// └── server.js
Express automatically sets the Content-Type to text/plain
for .txt files served via express.static(). To add a Cache-Control header, pass maxAge in the static options:
app.use(express.static('public', { maxAge: '1h' })); Option 2: dedicated GET route
For full control over headers and content, add a dedicated route. This is also the right approach when the content is generated at runtime from a database or your API route registry.
// server.js — dedicated GET route
const express = require('express');
const app = express();
const llmsContent = `# Your Site
> One-sentence description of what your site or product does.
## Documentation
- [Getting Started](https://yoursite.com/docs/start): Install and configure in minutes.
- [API Reference](https://yoursite.com/docs/api): Full endpoint catalog with examples.
## Product
- [Overview](https://yoursite.com/product): Core features and capabilities.
- [Pricing](https://yoursite.com/pricing): Plans and billing details.
## Optional
- [Changelog](https://yoursite.com/changelog): Release history.
- [GitHub](https://github.com/your-org/your-repo): Source code.
`;
app.get('/llms.txt', (req, res) => {
res.type('text/plain');
res.set('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');
res.send(llmsContent);
});
app.listen(3000);
Place this route before any catch-all route handlers or 404 middleware, otherwise Express will never reach it.
TypeScript version
If your project uses TypeScript with @types/express, type the handler parameters
explicitly to avoid implicit any errors:
// server.ts — TypeScript version with typed Request/Response
import express, { Request, Response } from 'express';
const app = express();
const llmsContent = `# Your Site
> One-sentence description of your product or service.
## Documentation
- [Getting Started](https://yoursite.com/docs/start): Quick setup guide.
- [API Reference](https://yoursite.com/docs/api): Full endpoint catalog.
## Optional
- [Changelog](https://yoursite.com/changelog): Release history.
`;
app.get('/llms.txt', (req: Request, res: Response): void => {
res.set({
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
});
res.send(llmsContent);
});
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
Dynamic generation from routes metadata
For larger applications, maintain a publicRoutes array alongside your route definitions.
The /llms.txt handler maps this array to Markdown links — ensuring the file stays in
sync with your actual routes.
// server.js — generate llms.txt dynamically from route metadata
const express = require('express');
const app = express();
// Define your public routes with metadata
const publicRoutes = [
{ title: 'Getting Started', path: '/docs/start', description: 'Install and configure in minutes.' },
{ title: 'API Reference', path: '/docs/api', description: 'Full endpoint catalog with examples.' },
{ title: 'Authentication', path: '/docs/auth', description: 'OAuth 2.0 and API key setup.' },
{ title: 'Webhooks', path: '/docs/webhooks', description: 'Event payloads and retry policy.' },
{ title: 'Pricing', path: '/pricing', description: 'Plans and billing details.' },
];
const SITE_URL = process.env.SITE_URL || 'https://yoursite.com';
app.get('/llms.txt', (req, res) => {
const links = publicRoutes
.map((r) => `- [${r.title}](${SITE_URL}${r.path}): ${r.description}`)
.join('\n');
const body = [
'# Your Site',
'',
'> One-sentence description of your product.',
'',
'## Documentation',
'',
links,
].join('\n');
res.set({
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
});
res.send(body);
});
app.listen(3000);
Set SITE_URL as an environment variable so the same code works across local, staging,
and production environments.
Cache-Control header
Always add a Cache-Control header. A one-hour TTL with stale-while-revalidate is a sensible default — it allows reverse proxies and CDNs (nginx,
Cloudflare, AWS CloudFront) to cache the response and serve stale content while revalidating in the
background:
res.set('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');
If you use a CDN in front of Express, check that the CDN respects Cache-Control from the origin. Cloudflare honors it by default; AWS CloudFront requires
a cache policy that allows origin headers to pass through.
Verify
After starting your server, confirm the file is served correctly:
# Check headers
curl -I http://localhost:3000/llms.txt
# Expected:
# HTTP/1.1 200 OK
# Content-Type: text/plain; charset=utf-8
# Cache-Control: public, max-age=3600
# Check content
curl http://localhost:3000/llms.txt | head -5
# Should print: # Your Site After deploying, run the same check against your live URL, then paste it into the llms.txt validator for full spec compliance.
Checklist before shipping
- File served at
/llms.txtwith200 OK. Content-Type: text/plain; charset=utf-8is set.Cache-Controlheader is present.- Exactly one H1 heading at the top of the file.
- Blockquote summary immediately after the H1.
- All URLs are absolute (
https://). - Route is registered before any catch-all or 404 handlers.
- Validator returns no errors: llmtxt.info/validator/
Related guides
- How to create llms.txt — templates and checklist.
- llms.txt format reference — spec details.
- Best practices — what to include and what to skip.
- Validator · Generator.