CDN and cache layers | laravel-markdown-response | Spatie

 SPATIE

  Laravel Markdown Response
============================

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Laravel-markdown-response](https://spatie.be/docs/laravel-markdown-response/v1)  Advanced-usage  CDN and cache layers

 Version   v1

 Other versions for crawler [v1](https://spatie.be/docs/laravel-markdown-response/v1)

- [ Introduction ](https://spatie.be/docs/laravel-markdown-response/v1/introduction)
- [ Requirements ](https://spatie.be/docs/laravel-markdown-response/v1/requirements)
- [ Install the package ](https://spatie.be/docs/laravel-markdown-response/v1/installation-setup)
- [ Questions &amp; issues ](https://spatie.be/docs/laravel-markdown-response/v1/questions-issues)
- [ Support us ](https://spatie.be/docs/laravel-markdown-response/v1/support-us)
- [ About us ](https://spatie.be/docs/laravel-markdown-response/v1/about-us)
- [ Changelog ](https://spatie.be/docs/laravel-markdown-response/v1/changelog)

Basic usage
-----------

- [ Serve markdown to AI agents ](https://spatie.be/docs/laravel-markdown-response/v1/basic-usage/serve-markdown-to-ai-agents)
- [ Cache converted responses ](https://spatie.be/docs/laravel-markdown-response/v1/basic-usage/cache-converted-responses)
- [ Choose a conversion driver ](https://spatie.be/docs/laravel-markdown-response/v1/basic-usage/choose-a-conversion-driver)
- [ Test your setup ](https://spatie.be/docs/laravel-markdown-response/v1/basic-usage/test-your-setup)

Advanced usage
--------------

- [ Clean up HTML with preprocessors ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/clean-up-html-with-preprocessors)
- [ Clean up markdown with postprocessors ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/clean-up-markdown-with-postprocessors)
- [ Customize request detection ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/customize-request-detection)
- [ Listen to conversion events ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/listen-to-conversion-events)
- [ Convert HTML directly ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/convert-html-directly)
- [ CDN and cache layers ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/cdn-and-cache-layers)
- [ Response headers ](https://spatie.be/docs/laravel-markdown-response/v1/advanced-usage/response-headers)

 CDN and cache layers
====================

###  On this page

1. [ Cloudflare ](#content-cloudflare)
2. [ Other CDNs ](#content-other-cdns)
3. [ spatie/laravel-responsecache ](#content-spatielaravel-responsecache)
4. [ General advice ](#content-general-advice)

When you serve both HTML and markdown from the same URL, external cache layers can cause problems. The package includes a `Vary: Accept` header on markdown responses, but not all CDNs respect it.

Cloudflare
--------------------------------------------------------------------------------------

Cloudflare's Free and Pro plans do not respect the `Vary: Accept` header for non-image content types. This means the first response (HTML or markdown) gets cached and served to all subsequent requests — regardless of their Accept header.

If you use the `.md` suffix detection method, this is not an issue: `/about` and `/about.md` are different URLs with separate cache entries.

If you rely on Accept header or user agent detection, you need to configure Cloudflare to bypass its cache for markdown requests. Create a Cache Rule with this expression:

```
any(http.request.headers["accept"][*] contains "text/markdown")
```

Set the action to **Bypass Cache**.

For user agent-based detection, add expressions for the bots you want to handle:

```
any(http.request.headers["accept"][*] contains "text/markdown") or
http.request.headers["user-agent"] contains "GPTBot" or
http.request.headers["user-agent"] contains "ClaudeBot" or
http.request.headers["user-agent"] contains "ChatGPT-User"
```

Other CDNs
--------------------------------------------------------------------------------------

CDNs like Fastly, Varnish, and Nginx generally respect the `Vary: Accept` header. Since the package sets this header on markdown responses, these CDNs will store separate cache entries for different Accept header values without additional configuration.

spatie/laravel-responsecache
------------------------------------------------------------------------------------------------------------------------------------------

If you use [spatie/laravel-responsecache](https://github.com/spatie/laravel-responsecache), cached HTML responses may be served before this package's middleware runs. The response cache stores the first response it sees (HTML) and replays it for all subsequent requests — including those that should receive markdown.

To fix this, override `useCacheNameSuffix()` in your `CacheProfile` to create separate cache entries:

```
use Illuminate\Http\Request;
use Spatie\ResponseCache\CacheProfiles\CacheAllSuccessfulGetRequests;

class CacheProfile extends CacheAllSuccessfulGetRequests
{
    public function useCacheNameSuffix(Request $request): string
    {
        $suffix = parent::useCacheNameSuffix($request);

        if ($request->attributes->get('markdown-response.suffix')
            || str_contains($request->header('Accept', ''), 'text/markdown')
        ) {
            return $suffix . '-markdown';
        }

        return $suffix;
    }
}
```

Then register it in `config/responsecache.php`:

```
'cache_profile' => App\CacheProfiles\CacheProfile::class,
```

General advice
--------------------------------------------------------------------------------------------------

Caching failures are silent — a cached HTML response served to an AI bot is still a valid HTTP response, so no errors are logged. Test your setup end-to-end in production-like environments where all cache layers are active.
