schaefersoft / laravel-llms-txt
Automatically generate llms.txt and llms-full.txt for Laravel applications
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.0
- illuminate/cache: ^10.0|^11.0|^12.0|^13.0
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/filesystem: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/routing: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^2.0|^3.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0
This package is auto-updated.
Last update: 2026-03-26 10:53:57 UTC
README
Automatically generate llms.txt and llms-full.txt for Laravel applications, making your site more accessible to AI models and LLM-powered tools.
What is llms.txt?
The llms.txt standard defines a simple Markdown file that helps AI models understand the structure and content of a website — similar to how sitemap.xml helps search engines. This package provides a fluent PHP API and an Artisan command to generate and serve these files from your Laravel application.
Requirements
- PHP 8.2+
- Laravel 10, 11, 12, or 13
Installation
Install via Composer:
composer require schaefersoft/laravel-llms-txt
The package is auto-discovered by Laravel. Publish the config file:
php artisan vendor:publish --tag=llms-txt-config
Configuration
After publishing, edit config/llms-txt.php:
return [ // Enable/disable the dynamic /llms.txt and /llms-full.txt routes 'route_enabled' => true, 'llms_txt_route' => '/llms.txt', 'llms_full_txt_route' => '/llms-full.txt', // Cache the generated output 'cache_enabled' => true, 'cache_ttl' => 3600, // seconds // Filesystem disk for static file generation (php artisan llms:generate) 'disk' => 'public', // Localization 'locales' => ['de', 'en'], // supported locales 'localize_routes' => false, // register /de/llms.txt etc. ];
Usage
Binding your LlmsTxt definition
The recommended approach is to bind your LlmsTxt instance in a service provider so it is available for both dynamic routes and the Artisan command:
// app/Providers/AppServiceProvider.php use SchaeferSoft\LaravelLlmsTxt\Entry; use SchaeferSoft\LaravelLlmsTxt\LlmsTxt; use SchaeferSoft\LaravelLlmsTxt\Section; public function register(): void { $this->app->bind(LlmsTxt::class, function () { return LlmsTxt::create() ->title('SchaeferSoft') ->description('Web development and software agency') ->addSection( Section::create('Services') ->addEntry(Entry::create( 'Web Development', 'https://schaefersoft.ch/services/web', 'Modern web applications with Laravel and Vue.js', )) ->addEntry(Entry::create( 'Hosting', 'https://schaefersoft.ch/services/hosting', 'Managed hosting and DevOps', )) ) ->addSection( Section::create('References') ->addEntry(Entry::create( 'Our Projects', 'https://schaefersoft.ch/references', 'Overview of all client projects', )) ); }); }
Once bound, visiting /llms.txt returns:
# SchaeferSoft
> Web development and software agency
## Services
- [Web Development](https://schaefersoft.ch/services/web): Modern web applications with Laravel and Vue.js
- [Hosting](https://schaefersoft.ch/services/hosting): Managed hosting and DevOps
## References
- [Our Projects](https://schaefersoft.ch/references): Overview of all client projects
Extracting the binding into a dedicated service provider
As your LlmsTxt definition grows, you may want to move it out of AppServiceProvider into its own file. Create a dedicated provider:
php artisan make:provider LlmsTxtServiceProvider
// app/Providers/LlmsTxtServiceProvider.php namespace App\Providers; use Illuminate\Support\ServiceProvider; use SchaeferSoft\LaravelLlmsTxt\Entry; use SchaeferSoft\LaravelLlmsTxt\LlmsTxt; use SchaeferSoft\LaravelLlmsTxt\Section; class LlmsTxtServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(LlmsTxt::class, function () { return LlmsTxt::create() ->title('SchaeferSoft') ->description('Web development and software agency') ->addSection( Section::create('Services') ->addEntry(Entry::create( 'Web Development', 'https://schaefersoft.ch/services/web', 'Modern web applications with Laravel and Vue.js', )) ); }); } }
Then register it in bootstrap/providers.php:
return [ App\Providers\AppServiceProvider::class, App\Providers\LlmsTxtServiceProvider::class, ];
Static file generation
Use the Artisan command to write static files to the public/ directory:
# Generate public/llms.txt php artisan llms:generate # Generate both public/llms.txt and public/llms-full.txt php artisan llms:generate --full # Generate for a specific locale (writes to public/de/llms.txt) php artisan llms:generate --locale=de # Generate for all locales defined in config php artisan llms:generate --all-locales # Combine flags php artisan llms:generate --all-locales --full
Programmatic usage
You can also call the builder directly without binding it in the container:
use SchaeferSoft\LaravelLlmsTxt\Entry; use SchaeferSoft\LaravelLlmsTxt\LlmsTxt; use SchaeferSoft\LaravelLlmsTxt\Section; $content = LlmsTxt::create() ->title('SchaeferSoft') ->description('Web development agency') ->addSection( Section::create('Services') ->addEntry(Entry::create('Web Development', 'https://schaefersoft.ch/services/web')) ) ->render(); // Write to disk directly LlmsTxt::create() ->title('SchaeferSoft') ->writeToDisk(); // writes to the configured disk as llms.txt // Or specify a custom filename LlmsTxt::create() ->title('SchaeferSoft') ->writeToDisk('custom/path/llms.txt');
Localization
Per-entry and per-section locale
Attach a locale to any Entry or Section:
Section::create('Leistungen', 'de') ->addEntry(Entry::create( 'Webentwicklung', 'https://schaefersoft.ch/de/leistungen', 'Moderne Webanwendungen mit Laravel und Vue.js', 'de', ));
Locale-prefixed routes
Enable locale-prefixed routes in config/llms-txt.php:
'locales' => ['de', 'en'], 'localize_routes' => true,
This registers /de/llms.txt, /en/llms.txt, /de/llms-full.txt, and /en/llms-full.txt.
Locale-aware registration with LlmsTxtRegistry
For multilingual sites, use LlmsTxtRegistry::forLocale() to register a separate builder per locale. The package sets app()->getLocale() before resolving the instance, so the correct factory is picked automatically — including on the default route (/llms.txt) which uses your app's default locale.
// app/Providers/LlmsTxtServiceProvider.php use SchaeferSoft\LaravelLlmsTxt\Entry; use SchaeferSoft\LaravelLlmsTxt\LlmsTxt; use SchaeferSoft\LaravelLlmsTxt\LlmsTxtRegistry; use SchaeferSoft\LaravelLlmsTxt\Section; public function register(): void { LlmsTxtRegistry::forLocale('de', fn() => LlmsTxt::create() ->title('SchaeferSoft') ->description('Webentwicklung und Softwareagentur') ->addSection( Section::create('Leistungen') ->addEntry(Entry::create( 'Webentwicklung', 'https://schaefersoft.ch/leistungen/web', 'Moderne Webanwendungen mit Laravel und Vue.js', )) ) ); LlmsTxtRegistry::forLocale('en', fn() => LlmsTxt::create() ->title('SchaeferSoft') ->description('Web development and software agency') ->addSection( Section::create('Services') ->addEntry(Entry::create( 'Web Development', 'https://schaefersoft.ch/en/services/web', 'Modern web applications with Laravel and Vue.js', )) ) ); }
/llms.txtserves the factory for your app's default locale (e.g.'de')/en/llms.txtserves the'en'factory
The registry takes priority over any app()->bind(LlmsTxt::class, ...) binding.
Writing to disk with a locale set will automatically use a locale-prefixed path:
LlmsTxt::create() ->title('SchaeferSoft') ->locale('de') ->writeToDisk(); // writes to de/llms.txt on the configured disk
llms-full.txt
The extended llms-full.txt format fetches the content of each entry URL and appends it below the entry. This gives AI models access to the full text of each linked page.
$content = LlmsTxt::create() ->title('SchaeferSoft') ->addSection( Section::create('Services') ->addEntry(Entry::create('Web Development', 'https://schaefersoft.ch/services/web')) ) ->renderFull(); // performs HTTP requests for each entry URL // Or write directly to disk LlmsTxt::create() ->title('SchaeferSoft') ->writeFullToDisk();
Entries whose URLs cannot be fetched are silently skipped.
Caching
When cache_enabled is true in config, the rendered output is cached using Laravel's cache system:
// Render and cache (uses the key 'llms-txt' by default) $content = LlmsTxt::create()->title('SchaeferSoft')->getCached(); // Custom cache key $content = LlmsTxt::create()->title('SchaeferSoft')->getCached('my-llms-cache-key'); // Flush the cache LlmsTxt::create()->flushCache('my-llms-cache-key');
Output format
llms.txt
# Site Title
> Site description/tagline
## Section Name
- [Entry Title](https://url.com): Entry description
## Another Section
- [Entry Title](https://url.com): Entry description
llms-full.txt
Same as llms.txt, but the fetched content of each URL is appended below its entry line.
Testing
composer test
Changelog
Please see CHANGELOG for recent changes.
Contributing
Please see CONTRIBUTING for guidelines.
License
The MIT License (MIT). Please see License File for more information.