Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Markdown Response Laravel Package

spatie/laravel-markdown-response

Serve markdown versions of your Laravel HTML pages for AI agents and bots. Detect markdown requests via Accept: text/markdown, known user agents, or .md URLs. Driver-based conversion (local PHP or Cloudflare Workers AI), with caching and HTML preprocessing.

View on GitHub
Deep Wiki
Context7

title: Install the package weight: 3

You can install the package via composer:

composer require spatie/laravel-markdown-response

The package registers itself automatically.

Register the middleware

Add the ProvideMarkdownResponse middleware to the routes you want to serve as markdown:

use Spatie\MarkdownResponse\Middleware\ProvideMarkdownResponse;

Route::middleware(ProvideMarkdownResponse::class)->group(function () {
    Route::get('/', [HomeController::class, 'index']);
    Route::get('/about', [PageController::class, 'show']);
});

Or apply it globally to all routes.

A note on global URL rewriting

The package automatically registers a lightweight global middleware (RewriteMarkdownUrls) that strips .md suffixes from URLs before route matching. This is what makes /about.md resolve to your /about route. Because it must run before routing, it's always registered as global middleware — even when you apply ProvideMarkdownResponse per-route.

The middleware short-circuits immediately for non-.md requests, so the overhead is negligible. If you don't need .md suffix detection at all, you can disable this middleware in the config:

// config/markdown-response.php
'detection' => [
    'detect_via_md_suffix' => false,
],

Publish the config file

Optionally, you can publish the config file:

php artisan vendor:publish --tag="markdown-response-config"

The default League driver converts HTML to markdown locally and works without any external services. The Cloudflare driver offers better conversion quality. You can choose a different driver at any time.

This is the content of the published config file:

return [

    /*
     * When disabled, the middleware will not convert any responses to markdown.
     */
    'enabled' => env('MARKDOWN_RESPONSE_ENABLED', true),

    /*
     * The driver used to convert HTML to markdown.
     * Supported: "league", "cloudflare"
     */
    'driver' => env('MARKDOWN_RESPONSE_DRIVER', 'league'),

    'detection' => [

        /*
         * The class responsible for detecting whether a request wants
         * a markdown response. You can extend the default class to
         * customize the detection logic.
         */
        'detector' => DetectsMarkdownRequest::class,

        /*
         * When enabled, requests with an `Accept: text/markdown` header
         * will receive a markdown response.
         */
        'detect_via_accept_header' => true,

        /*
         * When enabled, URLs ending in `.md` (e.g. `/about.md`) will
         * receive a markdown response. The `.md` suffix is stripped
         * before routing, so `/about.md` resolves to `/about`.
         */
        'detect_via_md_suffix' => true,

        /*
         * Requests from user agents containing any of these strings
         * will automatically receive a markdown response. Matching
         * is case-insensitive.
         */
        'detect_via_user_agents' => [
            'GPTBot',
            'ClaudeBot',
            'Claude-Web',
            'Anthropic',
            'ChatGPT-User',
            'PerplexityBot',
            'Bytespider',
            'Google-Extended',
        ],
    ],

    /*
     * Preprocessors are run on the HTML before it is converted to
     * markdown. Each class must implement the Preprocessor interface.
     */
    'preprocessors' => [
        RemoveScriptsAndStylesPreprocessor::class,
    ],

    /*
     * Postprocessors are run on the markdown after conversion.
     * Each class must implement the Postprocessor interface.
     */
    'postprocessors' => [
        RemoveHtmlTagsPostprocessor::class,
        CollapseBlankLinesPostprocessor::class,
    ],

    'cache' => [

        /*
         * When enabled, converted markdown responses will be cached
         * so subsequent requests skip the conversion entirely.
         */
        'enabled' => env('MARKDOWN_RESPONSE_CACHE_ENABLED', true),

        /*
         * The cache store to use. Set to null to use the default store.
         */
        'store' => env('MARKDOWN_RESPONSE_CACHE_STORE'),

        /*
         * How long converted markdown should be cached, in seconds.
         */
        'ttl' => (int) env('MARKDOWN_RESPONSE_CACHE_TTL', 3600),

        /*
         * The class responsible for generating cache keys from requests.
         * You can extend the default class to customize the key generation.
         */
        'key_generator' => GeneratesCacheKey::class,

        /*
         * These query parameters will be stripped when generating cache
         * keys, so the same page with different tracking parameters
         * shares a single cache entry.
         */
        'ignored_query_parameters' => [
            'utm_source',
            'utm_medium',
            'utm_campaign',
            'utm_term',
            'utm_content',
            'gclid',
            'fbclid',
        ],
    ],

    'driver_options' => [

        /*
         * The league driver uses league/html-to-markdown.
         * Options are passed directly to the HtmlConverter constructor.
         * See: https://github.com/thephpleague/html-to-markdown#options
         */
        'league' => [
            'options' => [
                'strip_tags' => true,
                'hard_break' => true,
            ],
        ],

        /*
         * The Cloudflare driver uses the Workers AI API to convert
         * HTML to markdown. Requires an account ID and API token.
         */
        'cloudflare' => [
            'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
            'api_token' => env('CLOUDFLARE_API_TOKEN'),
        ],

    ],
];
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport