llms.txt for Laravel
Laravel's public/ directory is the web root — zero config. For dynamic generation, add a route in routes/web.php or a dedicated controller with Laravel's Cache facade.
Last updated:
Option 1: static file in public/
Laravel’s public/ directory is the document root served by nginx or Apache. Any
file placed there is available at the corresponding URL path with no routing configuration. This is
the simplest approach and requires zero code changes.
# Laravel static file approach
#
# Laravel's public/ directory is the web root (served by nginx/Apache).
# Drop your file here and it is immediately available at /llms.txt.
#
# Project structure:
# your-laravel-app/
# ├── public/
# │ ├── index.php
# │ └── llms.txt ← add this
# ├── routes/
# └── app/
#
# No code change needed. Works on Forge, Vapor, Heroku, and bare VPS.
This approach works with all Laravel deployment targets: Laravel Forge, Laravel Vapor, Heroku, Railway, and bare VPS servers. The web server (nginx/Apache) serves the file directly without involving PHP — which is also faster.
Option 2: route in routes/web.php
When you want to generate the content programmatically or manage it from code rather than a
file, add a named route in routes/web.php:
<?php
// routes/web.php — serve llms.txt via a named route
use Illuminate\Support\Facades\Route;
Route::get('/llms.txt', function () {
$content = <<<'LLMS'
# 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.
LLMS;
return response($content, 200)
->header('Content-Type', 'text/plain; charset=utf-8')
->header('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');
})->name('llms-txt');
Use PHP’s heredoc syntax (<<<'LLMS') to write the content inline
without worrying about escaping. The single-quoted heredoc marker means no variable
interpolation — the content is treated as a literal string.
Option 3: controller with caching
For content generated dynamically from the database — for example, pulling published
documentation pages — use a dedicated invokable controller with Cache::remember() to avoid a database query on every request:
<?php
// app/Http/Controllers/LlmsTxtController.php
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
class LlmsTxtController extends Controller
{
public function __invoke(): Response
{
// Cache for 1 hour — regenerates automatically when expired
$content = Cache::remember('llms_txt', 3600, function () {
return $this->buildContent();
});
return response($content, 200)
->header('Content-Type', 'text/plain; charset=utf-8')
->header('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');
}
private function buildContent(): string
{
// You can query your database here:
// $docs = \App\Models\Doc::published()->get();
// $links = $docs->map(fn($d) => "- [{$d->title}](https://yoursite.com/docs/{$d->slug}): {$d->summary}")->join("\n");
return <<<'LLMS'
# Your Site
> One-sentence description of your product.
## 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.
## Optional
- [Changelog](https://yoursite.com/changelog): Release history.
LLMS;
}
}
<?php
// routes/web.php — register the controller route
use App\Http\Controllers\LlmsTxtController;
use Illuminate\Support\Facades\Route;
Route::get('/llms.txt', LlmsTxtController::class)->name('llms-txt');
Cache::remember() stores the result in your configured cache driver (Redis, Memcached,
database, or file). The cache is regenerated automatically after 3600 seconds. To invalidate immediately
after a content update, call Cache::forget('llms_txt') in your model’s observer or after a deployment hook.
Response macro for reusability
If you serve multiple plain-text files or want a consistent pattern across your application,
register a plaintext() response macro in AppServiceProvider:
<?php
// app/Providers/AppServiceProvider.php — response macro for reusability
namespace App\Providers;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Register a plaintext() macro for serving text/plain responses
Response::macro('plaintext', function (string $content, int $maxAge = 3600) {
return Response::make($content, 200, [
'Content-Type' => 'text/plain; charset=utf-8',
'Cache-Control' => "public, max-age={$maxAge}, stale-while-revalidate=86400",
]);
});
}
}
// Usage in a route or controller:
// return response()->plaintext($content);
The macro is then available anywhere in your application via response()->plaintext($content), keeping the Content-Type and Cache-Control headers consistent.
Verify
After deploying, confirm the file is served correctly:
curl -I https://yoursite.com/llms.txt
# Expected:
# HTTP/2 200
# content-type: text/plain; charset=utf-8
# cache-control: public, max-age=3600
curl https://yoursite.com/llms.txt | head -5
# Should print: # Your Site Then paste the URL into the llms.txt validator for full spec compliance checking.
Checklist before shipping
- File served at
/llms.txtwith200 OK. Content-Type: text/plain; charset=utf-8is set.Cache-Controlheader is present.- Response is plain text — no HTML, no Blade output.
- Exactly one H1 heading at the top.
- Blockquote summary immediately after the H1.
- All URLs are absolute (
https://). - No auth middleware applied to the
/llms.txtroute. - Validator returns no errors: llmtxt.info/validator/
Related guides
- How to create llms.txt — templates and checklist.
- llms.txt format reference — spec details.
- llms.txt for headless CMS — Contentful, Sanity, Strapi.
- Best practices — what to include and what to skip.
- Validator · Generator.