Atomic locks — Cache::lock() for distributed mutex
Advanced5 min read·lv-20-004
interviewperformance
Concept
HTTP response caching is separate from application-level caching. It controls browser and CDN caching of HTTP responses, reducing round-trips to the server entirely.
Cache-Control header: The primary mechanism. Directives:
public: Can be cached by browsers and CDNs.private: Only cached by the browser (user-specific data).no-store: Never cache (sensitive data).no-cache: Cache but must revalidate before use.max-age=N: Cache for N seconds.s-maxage=N: CDN cache duration (overrides max-age for shared caches).immutable: Tell the browser the resource will never change (for versioned assets).
ETags: Server generates a hash of the response content. Browser sends If-None-Match: "hash" on subsequent requests. If the hash matches, server returns 304 Not Modified (no body) — saves bandwidth.
Last-Modified: Last-Modified: Wed, 01 Jan 2025 00:00:00 GMT. Browser sends If-Modified-Since. Server returns 304 if unchanged.
Laravel response methods:
$response->setEtag(string $etag).$response->setLastModified(\DateTime $date).$response->setMaxAge(int $seconds).$response->setPublic()/$response->setPrivate().$response->isNotModified(Request $request): Check conditional headers and return 304 if unchanged.
CDN caching: CDNs (Cloudflare, CloudFront) cache public responses. Set Cache-Control: public, max-age=86400, s-maxage=3600 to cache at CDN for 1 hour regardless of browser cache.
Code Example
php
<?php
use Illuminate\Http\Request;
class ProductController extends Controller
{
// Public resource — suitable for CDN + browser caching
public function show(Request $request, Product $product): \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
{
$etag = md5($product->updated_at->timestamp . $product->id);
$lastModified = $product->updated_at;
$response = response()->json(new \App\Http\Resources\ProductResource($product));
$response->setEtag($etag);
$response->setLastModified($lastModified);
$response->setPublic();
$response->setMaxAge(300); // Browser: cache 5 minutes
$response->headers->set('s-maxage', '3600'); // CDN: cache 1 hour
if ($response->isNotModified($request)) {
return $response->setStatusCode(304); // No body — saves bandwidth
}
return $response;
}
// User-specific — private cache
public function profile(Request $request): \Illuminate\Http\JsonResponse
{
$user = $request->user();
$response = response()->json(new \App\Http\Resources\UserResource($user));
$response->setPrivate();
$response->setMaxAge(60); // Browser only, 1 minute
$response->setEtag(md5($user->updated_at->timestamp));
if ($response->isNotModified($request)) {
return $response->setStatusCode(304);
}
return $response;
}
}
// Middleware to add cache headers globally
class AddCacheControlHeaders
{
public function handle(Request $request, \Closure $next): mixed
{
$response = $next($request);
// Authenticated routes — never cache
if (auth()->check()) {
$response->headers->set('Cache-Control', 'private, no-store');
return $response;
}
// Guest GET requests — allow public cache
if ($request->isMethod('GET')) {
$response->headers->set('Cache-Control', 'public, max-age=60, s-maxage=300');
}
return $response;
}
}