develupers / laravel-cdn-headers
Set proper cache-control headers for Cloudflare CDN to cache pages
Fund package maintenance!
Requires
- php: ^8.3|^8.4
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^3.0||^4.0
- pestphp/pest-plugin-arch: ^3.0||^4.0
- pestphp/pest-plugin-laravel: ^3.0||^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-10 03:33:03 UTC
README
Automatically set proper cache-control headers for CDN caching in Laravel applications. This package helps you optimize your application's performance by enabling CDN caching for public pages while ensuring personalized content remains private.
Features
- 🚀 Automatic cache-control header management
- 🔧 Flexible route-based configuration
- 🛡️ Automatic session cookie removal for cacheable responses
- 🔐 CSRF token removal for secure caching
- 🔄 Automatic CSRF token restoration via AJAX
- 🎯 Wildcard route patterns support
- 📊 Cloudflare integration for cache purging
- 🔍 Built-in debugging tools
- ⚡ Support for stale-while-revalidate and stale-if-error
- 🌐 Works with any CDN (Cloudflare, Fastly, CloudFront, etc.)
Requirements
- PHP 8.3 or higher
- Laravel 11.x or 12.x
Installation
Install via Composer
Install the package from Packagist by running this command in your Laravel application's root directory:
composer require develupers/laravel-cdn-headers
Laravel automatically discovers the service provider. No custom Composer repository or manual provider registration is required.
If you previously followed the GitHub installation instructions, remove the old repository entry before running the command above:
composer config --unset repositories.laravel-cdn-headers
Publish Configuration
After installation, publish the configuration file:
php artisan vendor:publish --provider="Develupers\CdnHeaders\CdnHeadersServiceProvider"
This will create a config/cdn-headers.php file where you can customize the package behavior.
Quick Start
-
Configure your cacheable routes in
config/cdn-headers.php:'routes' => [ 'home' => 300, // 5 minutes 'products.*' => 3600, // 1 hour for all product routes 'api.*' => 600, // 10 minutes for all API routes ],
-
Verify configuration with:
php artisan cdn-headers:status
-
Test a specific route to see if it will be cached:
php artisan cdn-headers:test /products
That's it! The middleware will automatically apply CDN headers to your configured routes.
Configuration
The configuration file will be published to config/cdn-headers.php:
return [ // Enable/disable the middleware 'enabled' => env('CDN_HEADERS_ENABLED', true), // Auto-register as global middleware or manual registration 'middleware_registration' => env('CDN_HEADERS_REGISTRATION', 'global'), // Skip authenticated users 'skip_authenticated' => true, // Remove cookies from cacheable responses 'remove_cookies' => true, // Define cacheable routes and their TTL in seconds 'routes' => [ 'home' => 300, // 5 minutes 'products.index' => 3600, // 1 hour 'products.show' => 7200, // 2 hours 'products.*' => 3600, // All product routes 'api.v1.*' => 600, // All API v1 routes ], // URL patterns (for routes without names) 'patterns' => [ '/blog/*' => 1800, '/api/v2/*' => 600, ], // Exclude specific routes 'excluded_routes' => [ 'admin.*', 'dashboard.*', ], // Security settings 'remove_csrf_tokens' => true, // Remove CSRF tokens from cached pages ];
Usage
Automatic Registration (Default)
By default, the middleware is automatically registered globally. Just configure your routes in the config file and you're ready to go!
Manual Registration
If you prefer manual control, set middleware_registration to 'manual' in the config, then register the middleware in your app/Http/Kernel.php:
// As global middleware (runs on all routes) protected $middleware = [ // ... other middleware \Develupers\CdnHeaders\Http\Middleware\CdnHeadersMiddleware::class, ];
Note: The middleware automatically detects authenticated users and sends appropriate cache headers (private for logged-in, public for anonymous).
Cloudflare Setup
When using Cloudflare as your CDN, you need to configure cache bypass rules for authenticated users. This prevents logged-in users from receiving cached anonymous pages.
Why Cloudflare Configuration is Needed
Cloudflare's edge servers don't process PHP or Laravel sessions. When a page is cached, Cloudflare serves it to everyone who requests that URL, regardless of authentication status. To fix this, you need to tell Cloudflare when to bypass the cache.
Step 1: Create Cache Eligibility Rules
By default, Cloudflare only caches static assets (JS, CSS, images). To enable HTML caching while respecting your Laravel cache headers:
-
Go to your Cloudflare Dashboard
-
Navigate to Caching → Cache Rules
-
Click Create rule
-
Configure the rule:
Rule name:
Enable HTML CachingWhen incoming requests match...
- Field:
URI Path - Operator:
equals(or usestarts withfor multiple paths) - Value:
/(or specify paths you want cached, e.g.,/song/*)
Then...
- Cache eligibility:
Eligible for cache - Edge TTL:
- Select:
Use cache-control header if present, bypass cache if not
- Select:
- Field:
-
Click Deploy
How this works:
- Cloudflare will respect the
Cache-Controlheaders from your Laravel application - Pages with
Cache-Control: publicget cached (anonymous users) - Pages with
Cache-Control: privatebypass cache (logged-in users) - Your
cdn-headers.phpconfig remains in control of cache durations
Step 2: Create Cache Bypass Rule for Authenticated Users
To ensure authenticated users always receive fresh, personalized content:
-
Go to your Cloudflare Dashboard
-
Navigate to Caching → Cache Rules
-
Click Create rule
-
Configure the rule:
Rule name:
Laravel Login BypassIf incoming requests match...
- Select:
Custom filter expression
When incoming requests match...
- Field:
Cookie - Operator:
contains - Value:
remember_web_
Then...
- Cache eligibility:
Bypass cache
- Select:
-
Click Deploy
Why this works: The remember_web_* cookie is only present for authenticated Laravel users. This rule ensures they always bypass Cloudflare's cache and get fresh content from your origin server.
Step 3: Understanding the Cookie Pattern
Laravel uses different cookies for session management:
laravel_session: Present for ALL users (authenticated and anonymous)remember_web_*: ONLY present for authenticated users who checked "Remember Me"XSRF-TOKEN: CSRF protection token (present for all users)
The remember_web_* cookie is the most reliable indicator of an authenticated user.
Alternative: Session-Based Bypass
If your application doesn't use "Remember Me" functionality, you may need to:
-
Set a custom cookie when users log in:
// In your login controller Cookie::queue('authenticated', '1', 60 * 24 * 30);
-
Clear it on logout:
// In your logout controller Cookie::queue(Cookie::forget('authenticated'));
-
Use this custom cookie in your Cloudflare rule instead
Step 4: Verify Your Configuration
After setting up the Cloudflare rule:
- Test as anonymous user: Page should be served from cache (
cf-cache-status: HIT) - Test as logged-in user: Page should bypass cache (
cf-cache-status: BYPASS) - Clear Cloudflare cache after making changes to ensure fresh start
Troubleshooting
Issue: Logged-in users still see cached pages
- Solution: Ensure the Cloudflare rule is active and matches your cookie pattern
- Check: Browser DevTools → Application → Cookies to verify cookie presence
Issue: Pages aren't being cached at all
- Solution: Check that
skip_authenticatedistruein your config - Verify: Response headers should include
Cache-Control: publicfor anonymous users
Issue: CSRF token errors on cached pages
- Solution: Enable
inject_csrf_loaderin config to dynamically load tokens - Note: Ensure your CSRF endpoint is excluded from caching
Important Notes
-
Two-Part Solution: You need BOTH:
- Cloudflare rules (to bypass cache for existing cached content)
- Proper middleware configuration (to prevent future bad caches)
-
Cache Purging: After implementing these changes, purge your Cloudflare cache to remove any incorrectly cached authenticated pages
-
Performance Impact: Authenticated users will always hit your origin server, which is intentional to serve personalized content
Artisan Commands
Check configuration status:
php artisan cdn-headers:status
Test a route:
php artisan cdn-headers:test /products
Clear CDN cache (Cloudflare):
php artisan cdn-headers:clear --all php artisan cdn-headers:clear --url=https://example.com/products
Security Considerations
CSRF Token Removal
When remove_csrf_tokens is enabled (default), the package automatically removes CSRF tokens from HTML responses before caching. This is crucial because:
- Security: Prevents all users from receiving the same CSRF token
- Cache Effectiveness: Allows CDNs to properly cache pages
- Session Independence: Cached pages work regardless of user sessions
CSRF tokens are removed from:
<meta name="csrf-token" content="...">tagswindow.Laravel = {"csrfToken": "..."}JavaScript objects- Other common CSRF token patterns
Important: Only enable caching for public pages that don't require CSRF protection. Pages with forms should typically not be cached.
CSRF Token Auto-Loading
When CSRF tokens are removed for caching, the package can automatically inject JavaScript to restore them via AJAX. This allows forms on cached pages to work correctly:
'inject_csrf_loader' => true, // Enable auto-injection 'csrf_loader_routes' => [ 'auto' => true, // Inject on any page where CSRF was removed // Or specify exact routes (when auto is false) 'routes' => [ 'contact.show', 'auth.login', 'auth.register', ], ], 'csrf_endpoint' => '/users/csrf-token', // Your CSRF endpoint
The injected script will:
- Fetch a fresh CSRF token from your endpoint
- Update the
<meta name="csrf-token">tag (or create it if it doesn't exist) - Set
window.Laravel.csrfToken - Update all form
_tokeninputs - Configure axios and jQuery AJAX headers
Note: You must have a CSRF token endpoint that returns JSON:
Route::get('/users/csrf-token', function () { return response()->json(['csrf_token' => csrf_token()]); });
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.