Serve LLM-optimized markdown versions of your Laravel pages by appending .md to any URL.
daikazu/laravel-llm-ready is a Laravel package for serve llm-optimized markdown versions of your laravel pages by appending .md to any url..
It currently has 2 GitHub stars and 396 downloads on Packagist (latest version v1.3.0).
Install it with composer require daikazu/laravel-llm-ready.
Discover more Laravel packages by daikazu
or browse all Laravel packages to compare alternatives.
Last updated
Large Language Models (LLMs) like ChatGPT and Claude work much better with clean markdown than parsing HTML. This package automatically converts your Laravel pages to markdown, making your site more accessible to AI crawlers, tools, and users who want to feed your content to LLMs.
.md to any URL or use ?format=md/llms.txt (llmstxt.org)Link header, @llmReady Blade directive, and llmReadyUrl() helper/blog -> /blog/)composer require daikazu/laravel-llm-ready
The package will auto-discover and register itself.
php artisan vendor:publish --tag=llm-ready-config
Your web server needs to pass .md requests to Laravel instead of trying to serve them as static files.
The default Laravel .htaccess should work. Ensure it contains:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Add this to your server block:
location ~ \.md$ {
try_files /index.php?$query_string =404;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
}
Works out of the box - no configuration needed.
There are three ways to get the markdown version of a page:
# Method 1: Append .md extension
https://yoursite.com/about-us.md
# Method 2: Use ?format=md query parameter
https://yoursite.com/?format=md
https://yoursite.com/about-us?format=md
# Method 3: Use index.md for index pages
https://yoursite.com/index.md # Home page
https://yoursite.com/blog/index.md # Blog index
The /llms.txt endpoint follows the llmstxt.org specification, providing LLMs with structured information about your site:
https://yoursite.com/llms.txt
Example output:
# Your Site Name
> A brief description of your site.
All pages on this site are available in markdown format for LLM consumption.
Append `.md` to any URL or add `?format=md` to get the markdown version.
## Documentation
[Getting Started](https://yoursite.com/docs/getting-started)
[API Reference](https://yoursite.com/docs/api)
## Pages
[Home](https://yoursite.com)
[About](https://yoursite.com/about)
[Blog](https://yoursite.com/blog)
LLMs and crawlers need a way to discover that a markdown version of a page exists. This package provides three complementary mechanisms:
Every HTML response automatically includes a Link header pointing to the markdown version:
Link: <https://yoursite.com/about.md>; rel="alternate"; type="text/markdown"
This is enabled by default. Disable it via config or environment variable:
LLM_READY_LINK_HEADER=false
Add a <link> tag to your templates so crawlers can discover the markdown version via HTML:
<head>
@llmReady
{{-- Outputs: <link rel="alternate" type="text/markdown" href="https://yoursite.com/about.md"> --}}
</head>
The directive is opt-in — it only outputs when you use it, and returns an empty string for excluded routes or when the package is disabled.
Use llmReadyUrl() in programmatic contexts like SEO middleware (e.g., romanzipp/laravel-seo):
// Returns "https://yoursite.com/about.md" or null if excluded/disabled
$markdownUrl = llmReadyUrl();
# Clear all LLM Ready caches
php artisan llm-ready:clear-cache
# Clear cache for a specific URL
php artisan llm-ready:clear-cache --url=https://yoursite.com/about-us
# Clear only the sitemap cache
php artisan llm-ready:clear-cache --sitemap
// config/llm-ready.php
return [
// Enable/disable the package
'enabled' => env('LLM_READY_ENABLED', true),
// Cache settings
'cache' => [
'enabled' => env('LLM_READY_CACHE_ENABLED', true),
'ttl' => env('LLM_READY_CACHE_TTL', 1440), // 24 hours in minutes
'prefix' => 'llm_ready',
],
// CSS selectors to find main content (tried in order)
'content_selectors' => [
'main',
'article',
'[role="main"]',
'.content',
],
// Elements to remove before conversion
'ignore_selectors' => [
'nav',
'header',
'footer',
'aside',
'.sidebar',
'script',
'style',
],
// Eyebrow/label text detection
'eyebrow_selectors' => [
'.eyebrow',
'.overline',
'.kicker',
'[class*="eyebrow"]',
],
'eyebrow_auto_detect' => true,
// Routes to exclude from markdown conversion (supports nested paths)
'exclude_patterns' => [
'/admin/*', // Matches /admin/users, /admin/settings/email, etc.
'/api/*', // Matches /api/v1/users, /api/webhooks/stripe, etc.
'/livewire/*',
],
// Markdown discovery settings
'discovery' => [
'link_header' => env('LLM_READY_LINK_HEADER', true),
],
// llms.txt configuration (follows llmstxt.org spec)
'llms_txt' => [
'enabled' => true,
'cache_ttl' => 60,
'title' => env('LLM_READY_LLMS_TXT_TITLE'), // Defaults to app name
'summary' => env('LLM_READY_LLMS_TXT_SUMMARY'),
'description' => [],
'sections' => [
// 'Documentation' => [
// ['url' => '/docs/intro.md', 'description' => 'Introduction'],
// ],
],
'auto_section' => [
'enabled' => true,
'title' => 'Pages',
],
],
// YAML frontmatter settings
'frontmatter' => [
'include_title' => true,
'include_description' => true,
'include_url' => true,
'include_last_modified' => true,
],
// Custom content extractor class
'extractor' => \Daikazu\LaravelLlmReady\Extractors\DefaultContentExtractor::class,
];
Requesting /about-us.md produces clean markdown:
---
title: About Us - Your Company Name
description: Learn about our mission and team
url: https://yoursite.com/about-us
last_modified: 2024-01-15T10:30:00+00:00
---
# About Us
Welcome to our company! We're dedicated to...
## Our Mission
We believe in creating value for our customers through...
## Our Team
| Name | Role | Experience |
|------|------|------------|
| Jane Doe | CEO | 15 years |
| John Smith | CTO | 12 years |
The llms.txt endpoint can be fully customized to provide curated information to LLMs:
'llms_txt' => [
'title' => 'Acme Documentation',
'summary' => 'Complete documentation for the Acme platform.',
'description' => [
'Acme helps developers build better applications faster.',
'Our documentation covers installation, configuration, and advanced usage.',
],
'sections' => [
'Getting Started' => [
['url' => '/docs/installation', 'description' => 'How to install Acme'],
['url' => '/docs/quickstart', 'description' => '5-minute quickstart guide'],
],
'API Reference' => [
['url' => '/docs/api/authentication', 'description' => 'Authentication methods'],
['url' => '/docs/api/endpoints', 'description' => 'Available API endpoints'],
],
],
'auto_section' => [
'enabled' => true,
'title' => 'All Pages',
'include_in_optional' => false,
],
],
Create your own extractor by implementing ContentExtractorInterface:
<?php
namespace App\LlmReady;
use Daikazu\LaravelLlmReady\Contracts\ContentExtractorInterface;
use DOMDocument;
class CustomContentExtractor implements ContentExtractorInterface
{
public function extract(DOMDocument $dom, array $contentSelectors, array $ignoreSelectors): string
{
// Your custom extraction logic
}
}
Then update your config:
'extractor' => \App\LlmReady\CustomContentExtractor::class,
LLM_READY_ENABLED=true
LLM_READY_CACHE_ENABLED=true
LLM_READY_CACHE_TTL=1440
LLM_READY_LLMS_TXT_ENABLED=true
LLM_READY_LLMS_TXT_CACHE_TTL=60
LLM_READY_LINK_HEADER=true
LLM_READY_LLMS_TXT_TITLE="My Site"
LLM_READY_LLMS_TXT_SUMMARY="A brief description of my site."
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.