GzipResponseMiddleware: compresion de respuestas
El ancho de banda es caro. Cada kilobyte cuenta cuando sirves millones de peticiones. La compresión gzip puede reducir el tamaño de una respuesta JSON típica entre un 70 y un 80 %. Pero comprimir cuesta ciclos de CPU. Este middleware es listo sobre cuándo comprimir: sólo lo hace cuando el beneficio supera al coste.
El middleware comprueba cinco condiciones antes de comprimir:
-
Sólo peticiones GET. La compresión tiene sentido en operaciones de lectura que devuelven datos.
-
Sólo respuestas correctas (200-299). Comprime datos, no mensajes de error.
-
El cliente debe aceptar gzip. Comprueba la cabecera
Accept-Encoding. Si no incluyegzip, el cliente no puede descomprimir. -
La respuesta no está ya codificada. Si otro middleware ya comprimió, no comprimas dos veces.
-
La respuesta es suficientemente grande. Sólo comprime a partir de 1.400 bytes. Comprimir respuestas diminutas añade una sobrecarga que supera el ahorro.
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// Only compress GET with success status (200-299)
if ($request->getMethod() !== 'GET') {
return $response;
}
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
return $response;
}
// Check if client accepts gzip
$acceptEncoding = $request->header('Accept-Encoding', '');
if (! str_contains($acceptEncoding, 'gzip')) {
return $response;
}
// Don't compress if already encoded
if ($response->headers->has('Content-Encoding')) {
return $response;
}
$content = $response->getContent();
// Only compress if content is large enough
if (strlen($content) < self::MIN_COMPRESSION_SIZE) {
return $response;
}
// Compress with gzip compression level 9 (maximum)
$compressed = gzencode($content, 9);
$response->setContent($compressed);
$response->headers->set('Content-Encoding', 'gzip');
return $response;
}
El middleware usa gzencode() con nivel 9, el máximo. Tarda más que el nivel 6 por defecto pero produce ficheros más pequeños. Para APIs, la CPU extra compensa el ahorro de ancho de banda, sobre todo en respuestas grandes.
Imagina un listado que devuelve cien licencias con todos sus detalles. La respuesta podría ser de 50 KB. Con gzip:
- Sin comprimir: 50 KB transmitidos
- Comprimido: unos 8 KB (85 % menos)
- Sobrecarga de compresión: típicamente unos pocos milisegundos de CPU
- Descompresión: automática en el cliente, se siente instantánea
Algunas respuestas no deberían comprimirse: las ya comprimidas (imágenes, PDFs, vídeos), las respuestas en streaming y los eventos enviados por el servidor. Para una API REST típica que devuelve JSON, comprimir casi siempre es beneficioso.
Y aquí está la magia: puedes añadir, quitar o reordenar middlewares sin cambiar el código de tus controladores.