php 57 lines · 8 steps

ETag caching and gzip in Laravel middleware

A Laravel middleware that adds conditional-request caching and gzip compression after the response is built.

Explained by highlit
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class CompressAndCacheResponse
10{
11 public function handle(Request $request, Closure $next): Response
12 {
13 $response = $next($request);
14 
15 if (!$this->shouldProcess($request, $response)) {
16 return $response;
17 }
18 
19 $body = $response->getContent();
20 $etag = '"' . md5($body) . '"';
21 $response->headers->set('ETag', $etag);
22 $response->headers->set('Cache-Control', 'private, must-revalidate, max-age=0');
23 
24 $ifNoneMatch = array_map('trim', explode(',', $request->headers->get('If-None-Match', '')));
25 
26 if (in_array($etag, $ifNoneMatch, true)) {
27 $response->setNotModified();
28 $response->headers->set('ETag', $etag);
29 return $response;
30 }
31 
32 if ($this->acceptsGzip($request) && strlen($body) > 1024) {
33 $compressed = gzencode($body, 6);
34 $response->setContent($compressed);
35 $response->headers->set('Content-Encoding', 'gzip');
36 $response->headers->set('Content-Length', (string) strlen($compressed));
37 $response->headers->set('Vary', 'Accept-Encoding');
38 }
39 
40 return $response;
41 }
42 
43 private function shouldProcess(Request $request, Response $response): bool
44 {
45 return $request->isMethod('GET')
46 && $response->isSuccessful()
47 && !$response->headers->has('Content-Encoding')
48 && str_contains((string) $response->headers->get('Content-Type'), 'text/')
49 || str_contains((string) $response->headers->get('Content-Type'), 'json');
50 }
51 
52 private function acceptsGzip(Request $request): bool
53 {
54 return str_contains($request->headers->get('Accept-Encoding', ''), 'gzip')
55 && function_exists('gzencode');
56 }
57}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Running logic after $next($request) lets middleware inspect and rewrite the fully-built response.
  2. 2An ETag derived from body content enables 304 responses that skip resending unchanged data.
  3. 3Operator precedence bugs like mixing && and || without parentheses can silently widen a guard clause.

Related explainers

Share this explainer

Here's the card — post it anywhere.

ETag caching and gzip in Laravel middleware — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code