spatie/laravel-responsecache
Cache full Laravel responses to speed up your app. Automatically caches successful text-based GET requests (HTML/JSON), with easy middleware per route, configurable lifetimes, and optional stale-while-revalidate “grace” caching to refresh in the background.
Installation:
composer require spatie/laravel-responsecache
Publish the config file (optional):
php artisan vendor:publish --provider="Spatie\ResponseCache\ResponseCacheServiceProvider" --tag="responsecache-config"
First Use Case: Cache a route for 10 minutes using middleware:
use Spatie\ResponseCache\Middlewares\CacheResponse;
Route::middleware(CacheResponse::for(minutes(10)))->group(function () {
Route::get('/posts', [PostController::class, 'index']);
});
Key Files to Review:
config/responsecache.php (for global settings)app/Http/Middleware/ (for custom middleware extensions)app/Providers/AppServiceProvider.php (for global cache configuration)Route-Level Caching:
// Cache for 1 hour with tags (for cache invalidation)
Route::middleware(CacheResponse::for(hours(1), tags: ['posts']))
->get('/posts', [PostController::class, 'index']);
Controller-Level Attributes (Laravel 8.43+):
use Spatie\ResponseCache\Attributes\Cache;
#[Cache(lifetime: '1 hour', tags: ['dashboard'])]
public function index() { ... }
Flexible Caching (Stale-While-Revalidate):
use Spatie\ResponseCache\Middlewares\FlexibleCacheResponse;
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware(FlexibleCacheResponse::for(
lifetime: hours(1),
grace: minutes(5)
));
Global Cache Configuration:
// In AppServiceProvider boot()
ResponseCache::shareCacheBetweenRequests();
ResponseCache::enableDebugHeaders();
Cache Invalidation: Use tags to invalidate related caches:
ResponseCache::forgetTags(['posts']); // Invalidate all 'posts' tagged caches
Conditional Caching: Skip caching for authenticated users:
Route::middleware([
'auth',
CacheResponse::for(minutes(10))->unless(fn ($request) => $request->user()->isAdmin())
])->get('/profile');
API Responses:
Cache JSON responses with CacheResponse::forApi():
Route::middleware(CacheResponse::forApi(minutes(5)))->get('/api/posts');
Event-Based Cache Clearing:
Listen to CacheMissed events to log cache misses:
event(new CacheMissed($request, $response));
Race Conditions:
shareCacheBetweenRequests() to avoid state issues in middleware.FlexibleCacheResponse for high-traffic endpoints to avoid stale data during cache refreshes.CSRF Tokens:
CsrfTokenReplacer:
ResponseCache::replaceCsrfTokenInCachedResponses();
Dynamic Content:
unless():
CacheResponse::for(minutes(10))->unless(fn ($request) => $request->user()->hasDynamicContent())
Debugging:
ResponseCache::enableDebugHeaders();
X-Response-Cache (HIT, MISS, STALE).Memory Usage:
file). For large-scale apps, use Redis:
ResponseCache::useRedis();
Tag-Based Invalidation:
Use tags for related resources (e.g., ['posts', 'categories']) to invalidate caches when data changes:
ResponseCache::forgetTags(['posts']); // After updating a post
Partial Caching:
Cache only specific parts of a response using CacheResponse::forPartial() (requires manual implementation).
Testing: Mock the cache in tests:
$this->actingAs($user)
->withGlobalCacheDisabled()
->get('/cached-route');
Performance Tuning:
xxh128 hasher (default) for faster cache key generation.cache_lifetime in config/responsecache.php for global defaults.Edge Cases:
CacheResponse::for(minutes(10))->except(fn ($request) => $request->is('redirects/*'));
Custom Storage: Extend the cache driver:
ResponseCache::useCustomStorage(function () {
return Cache::store('redis')->rememberForever(...);
});
Laravel 11+: Use attributes for cleaner code:
#[Cache(lifetime: '1 hour', tags: ['homepage'])]
public function show() { ... }
Cache Profiling:
Use ResponseCache::profile() to log cache hits/misses:
ResponseCache::profile();
How can I help you explore Laravel packages today?