Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Spatie Laravel Responsecache
https://github.com/spatie/laravel-responsecache
Admin
This Laravel package caches entire responses to speed up applications. It automatically caches
...
Tokens:
7,332
Snippets:
38
Trust Score:
8.5
Update:
5 months ago
Context
Skills
Chat
Benchmark
75.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel Response Cache Laravel Response Cache is a performance optimization package that speeds up Laravel applications by caching entire HTTP responses. When a request comes in, the middleware checks if a cached response exists and serves it immediately, bypassing the entire application stack including routing, controllers, and database queries. This can dramatically reduce response times from hundreds of milliseconds to just a few milliseconds for subsequent requests. The package is highly configurable and supports features like per-user caching, selective URL caching, cache tagging, custom cache profiles, CSRF token replacement, and programmatic cache management. It works with any Laravel cache driver and automatically handles serialization, cache key generation, and response restoration. By default, it caches all successful GET requests returning text-based content (HTML, JSON) for one week, but every aspect can be customized through configuration or custom implementations. ## Installation and Configuration ```bash # Install via Composer composer require spatie/laravel-responsecache # Publish configuration file php artisan vendor:publish --tag="responsecache-config" ``` ```php // config/responsecache.php return [ 'enabled' => env('RESPONSE_CACHE_ENABLED', true), 'cache_profile' => Spatie\ResponseCache\CacheProfiles\CacheAllSuccessfulGetRequests::class, 'cache_lifetime_in_seconds' => env('RESPONSE_CACHE_LIFETIME', 60 * 60 * 24 * 7), 'cache_store' => env('RESPONSE_CACHE_DRIVER', 'file'), 'add_cache_time_header' => env('APP_DEBUG', true), 'cache_time_header_name' => 'laravel-responsecache', 'replacers' => [ \Spatie\ResponseCache\Replacers\CsrfTokenReplacer::class, ], 'hasher' => \Spatie\ResponseCache\Hasher\DefaultHasher::class, 'serializer' => \Spatie\ResponseCache\Serializers\DefaultSerializer::class, 'cache_tag' => '', ]; ``` ```php // bootstrap/app.php (Laravel 11+) ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ \Spatie\ResponseCache\Middlewares\CacheResponse::class, ]); $middleware->alias([ 'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class, ]); }) ``` ```php // app/Http/Kernel.php (Laravel 10 and earlier) protected $middlewareGroups = [ 'web' => [ \Spatie\ResponseCache\Middlewares\CacheResponse::class, ], ]; protected $middlewareAliases = [ 'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class, ]; ``` ## Clear Entire Cache ```php use Spatie\ResponseCache\Facades\ResponseCache; // Clear all cached responses ResponseCache::clear(); // Clear responses with specific tags (requires cache driver that supports tags) ResponseCache::clear(['products', 'api']); ``` ```bash # Clear cache via Artisan command php artisan responsecache:clear # Clear cache for specific URL php artisan responsecache:clear --url=/products ``` ```php // Clear cache automatically on model events namespace App\Traits; use Spatie\ResponseCache\Facades\ResponseCache; trait ClearsResponseCache { public static function bootClearsResponseCache() { self::created(function () { ResponseCache::clear(); }); self::updated(function () { ResponseCache::clear(); }); self::deleted(function () { ResponseCache::clear(); }); } } // Apply to model class Product extends Model { use ClearsResponseCache; } ``` ## Forget Specific URLs ```php use Spatie\ResponseCache\Facades\ResponseCache; // Forget single URL ResponseCache::forget('/products/123'); // Forget multiple URLs (array syntax) ResponseCache::forget(['/products/123', '/products/456']); // Forget multiple URLs (variadic syntax) ResponseCache::forget('/products/123', '/products/456'); // Note: forget() only works without cacheNameSuffix // For suffix-based caching, use selectCachedItems() ``` ## Select and Forget Cache Items with Advanced Criteria ```php use Spatie\ResponseCache\Facades\ResponseCache; // Forget all PUT responses for specific URL ResponseCache::selectCachedItems() ->withPutMethod() ->forUrls('/products/update') ->forget(); // Forget multiple URLs with specific method ResponseCache::selectCachedItems() ->withPutMethod() ->forUrls(['/products/123', '/products/456']) ->forget(); // Forget with user-specific suffix (default: user ID or empty string) ResponseCache::selectCachedItems() ->usingSuffix('100') ->forUrls('/dashboard') ->forget(); // Complex selection with all criteria ResponseCache::selectCachedItems() ->withPutMethod() ->withHeaders(['Accept' => 'application/json']) ->withCookies(['session_token' => 'abc123']) ->withParameters(['page' => '1', 'limit' => '20']) ->withRemoteAddress('192.168.1.100') ->usingSuffix('100') ->usingTags('products', 'api') ->forUrls('/api/products', '/api/categories') ->forget(); ``` ## Prevent Specific Routes from Caching ```php // Apply middleware to single route Route::get('/auth/logout', function() { return redirect('/login'); })->middleware('doNotCacheResponse'); Route::get('/checkout', [CheckoutController::class, 'index']) ->middleware('doNotCacheResponse'); // Apply middleware to route group Route::middleware('doNotCacheResponse')->group(function () { Route::get('/admin/dashboard', [AdminController::class, 'dashboard']); Route::get('/admin/users', [AdminController::class, 'users']); }); ``` ```php // Apply in controller constructor class UserController extends Controller { public function __construct() { $this->middleware('doNotCacheResponse', [ 'only' => ['edit', 'update', 'destroy'] ]); $this->middleware('doNotCacheResponse', [ 'except' => ['index', 'show'] ]); } public function index() { // This WILL be cached return view('users.index'); } public function edit($id) { // This will NOT be cached return view('users.edit'); } } ``` ## Cache Specific Routes with Custom Lifetime ```php // Register as route middleware (if not globally applied) protected $middlewareAliases = [ 'cacheResponse' => \Spatie\ResponseCache\Middlewares\CacheResponse::class, ]; // Cache route for 5 minutes (300 seconds) Route::get('/trending', [ProductController::class, 'trending']) ->middleware('cacheResponse:300'); // Cache route group for 10 minutes (600 seconds) Route::middleware('cacheResponse:600')->group(function() { Route::get('/featured', [ProductController::class, 'featured']); Route::get('/bestsellers', [ProductController::class, 'bestsellers']); }); // Cache indefinitely until manually cleared Route::get('/static-content', [PageController::class, 'about']) ->middleware('cacheResponse:604800'); // 1 week ``` ## Use Cache Tags for Selective Clearing ```php use Spatie\ResponseCache\Middlewares\CacheResponse; // Add single tag with lifetime Route::get('/products', [ProductController::class, 'index']) ->middleware('cacheResponse:300,products'); // Add single tag without custom lifetime (uses default) Route::get('/categories', [CategoryController::class, 'index']) ->middleware('cacheResponse:categories'); // Add multiple tags with lifetime Route::middleware('cacheResponse:300,products,api')->group(function() { Route::get('/api/products', [ApiProductController::class, 'index']); Route::get('/api/products/{id}', [ApiProductController::class, 'show']); }); // Use static helper method for clarity Route::get('/featured', [ProductController::class, 'featured']) ->middleware(CacheResponse::using(300, 'products', 'featured')); ``` ```php // Clear only tagged cache entries ResponseCache::clear(['products']); // Clears /products and /api/products ResponseCache::clear(['products', 'api']); // Clears only items with BOTH tags // Alternative: Use Laravel's native cache tagging Cache::tags('products')->flush(); // Same effect ``` ## Bypass Cache Temporarily with Headers ```bash # Configure environment variables CACHE_BYPASS_HEADER_NAME=X-Skip-Cache CACHE_BYPASS_HEADER_VALUE=secret-key-12345 ``` ```bash # Request with bypass header gets fresh response curl -H "X-Skip-Cache: secret-key-12345" https://example.com/products # Without header, gets cached response curl https://example.com/products ``` ```php // Use in application code for debugging use Illuminate\Support\Facades\Http; $response = Http::withHeaders([ 'X-Skip-Cache' => config('responsecache.cache_bypass_header.value'), ])->get('https://example.com/api/products'); // Useful for profiling, debugging, or monitoring ``` ## Create Custom Cache Profile ```php namespace App\CacheProfiles; use DateTime; use Illuminate\Http\Request; use Spatie\ResponseCache\CacheProfiles\CacheProfile; use Symfony\Component\HttpFoundation\Response; use Carbon\Carbon; class CacheOnlyPublicPages implements CacheProfile { public function enabled(Request $request): bool { return config('responsecache.enabled'); } public function shouldCacheRequest(Request $request): bool { // Don't cache authenticated users if (auth()->check()) { return false; } // Only cache GET requests if (! $request->isMethod('get')) { return false; } // Don't cache AJAX requests if ($request->ajax()) { return false; } // Don't cache admin routes if ($request->is('admin/*')) { return false; } return true; } public function shouldCacheResponse(Response $response): bool { // Only cache successful responses if (! $response->isSuccessful()) { return false; } // Only cache HTML content $contentType = $response->headers->get('Content-Type', ''); if (! str_starts_with($contentType, 'text/html')) { return false; } return true; } public function cacheRequestUntil(Request $request): DateTime { // Cache homepage longer than other pages if ($request->path() === '/') { return Carbon::now()->addHours(24); } // Cache product pages for 1 hour if ($request->is('products/*')) { return Carbon::now()->addHours(1); } // Default: 30 minutes return Carbon::now()->addMinutes(30); } public function useCacheNameSuffix(Request $request): string { // Different cache per locale return app()->getLocale(); // Or per user role (for authenticated users) // return auth()->check() ? auth()->user()->role : 'guest'; // Or per device type // return $request->mobile() ? 'mobile' : 'desktop'; } } ``` ```php // config/responsecache.php 'cache_profile' => App\CacheProfiles\CacheOnlyPublicPages::class, ``` ## Create Custom Replacer for Dynamic Content ```php namespace App\Replacers; use Spatie\ResponseCache\Replacers\Replacer; use Symfony\Component\HttpFoundation\Response; class UserNameReplacer implements Replacer { protected string $placeholder = '<username-placeholder>'; public function prepareResponseToCache(Response $response): void { $content = $response->getContent(); if (! $content || ! auth()->check()) { return; } // Replace actual username with placeholder before caching $username = auth()->user()->name; $response->setContent( str_replace($username, $this->placeholder, $content) ); } public function replaceInCachedResponse(Response $response): void { $content = $response->getContent(); if (! $content || ! auth()->check()) { return; } // Replace placeholder with current user's name when serving cache $username = auth()->user()->name; $response->setContent( str_replace($this->placeholder, $username, $content) ); } } ``` ```php // config/responsecache.php 'replacers' => [ \Spatie\ResponseCache\Replacers\CsrfTokenReplacer::class, \App\Replacers\UserNameReplacer::class, ], ``` ```php // Another example: Current time replacer class CurrentTimeReplacer implements Replacer { protected string $placeholder = '<!--current-time-placeholder-->'; public function prepareResponseToCache(Response $response): void { $content = $response->getContent(); if (! $content) return; // Replace time elements with placeholder $response->setContent(preg_replace( '/<time class="current">[^<]*<\/time>/', '<time class="current">' . $this->placeholder . '</time>', $content )); } public function replaceInCachedResponse(Response $response): void { $content = $response->getContent(); if (! $content) return; // Insert current time when serving $response->setContent( str_replace($this->placeholder, now()->format('Y-m-d H:i:s'), $content) ); } } ``` ## Listen to Cache Events ```php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Spatie\ResponseCache\Events\ResponseCacheHit; use Spatie\ResponseCache\Events\CacheMissed; use Spatie\ResponseCache\Events\ClearingResponseCache; use Spatie\ResponseCache\Events\ClearedResponseCache; use Spatie\ResponseCache\Events\ClearingResponseCacheFailed; class EventServiceProvider extends ServiceProvider { protected $listen = [ ResponseCacheHit::class => [ LogCacheHit::class, ], CacheMissed::class => [ LogCacheMiss::class, ], ClearedResponseCache::class => [ NotifyAdminOfCacheClear::class, ], ]; } ``` ```php namespace App\Listeners; use Spatie\ResponseCache\Events\ResponseCacheHit; use Illuminate\Support\Facades\Log; class LogCacheHit { public function handle(ResponseCacheHit $event) { Log::info('Response cache hit', [ 'url' => $event->request->fullUrl(), 'method' => $event->request->method(), 'user_id' => auth()->id(), 'ip' => $event->request->ip(), ]); } } class LogCacheMiss { public function handle(\Spatie\ResponseCache\Events\CacheMissed $event) { Log::info('Response cache miss', [ 'url' => $event->request->fullUrl(), 'method' => $event->request->method(), ]); // Track cache miss rate for monitoring app('metrics')->increment('response_cache.misses'); } } ``` ## Programmatic Cache Management ```php use Spatie\ResponseCache\Facades\ResponseCache; use Illuminate\Http\Request; // Check if request has been cached $request = Request::create('/products'); if (ResponseCache::hasBeenCached($request)) { // Cache exists } // Check with tags if (ResponseCache::hasBeenCached($request, ['products'])) { // Tagged cache exists } // Get cached response directly $cachedResponse = ResponseCache::getCachedResponseFor($request); $cachedResponse = ResponseCache::getCachedResponseFor($request, ['products']); // Manually cache a response $request = Request::create('/products'); $response = response()->json(['products' => Product::all()]); // Cache for default duration ResponseCache::cacheResponse($request, $response); // Cache with custom lifetime (300 seconds) ResponseCache::cacheResponse($request, $response, 300); // Cache with tags ResponseCache::cacheResponse($request, $response, 300, ['products', 'api']); // Check if caching is enabled for request if (ResponseCache::enabled($request)) { // Proceed with caching logic } // Check if request should bypass cache if (ResponseCache::shouldBypass($request)) { // Skip cache and get fresh response } ``` ## Summary Laravel Response Cache provides a powerful yet simple solution for dramatically improving Laravel application performance through full-page caching. The package handles the complexities of caching entire HTTP responses including headers, status codes, and content while providing escape hatches for dynamic content through replacers. It's particularly effective for content-heavy sites, APIs with relatively static data, and applications where database queries are the primary bottleneck. The package integrates seamlessly with Laravel's ecosystem, supporting all cache drivers, working with authentication systems, and providing fine-grained control through cache profiles, tags, and selective forgetting. Whether you need to cache everything with minimal configuration or implement sophisticated per-user, per-locale, or per-route caching strategies, Laravel Response Cache offers the flexibility to match your application's specific requirements while maintaining clean, testable code.