Artículo

Caché HTTP: las tres configuraciones que uso casi siempre

Cache-Control, ETag y stale-while-revalidate sin la parte teórica. Tres casos que cubren casi cualquier sitio y un comando para comprobar que de verdad funcionan.

24 de agosto de 20263 min de lecturaEquipo ProTecnoAct. 3 de septiembre de 2026

Llevo años viendo la misma escena: alguien abre la especificación de caché HTTP, ve la cantidad de directivas que hay y decide que ya lo mirará otro día. Lo entiendo. Pero la verdad es que, en la práctica, casi todo se resuelve con tres configuraciones. El resto de la especificación existe para casos raros que probablemente no tengas.

Estos son los tres casos y lo que pongo en cada uno.

Los assets con hash en el nombre

Si tu build genera ficheros tipo app.4f3a9c.js, tienes suerte: ese fichero no va a cambiar nunca. Si cambia el contenido, cambia el nombre, así que no hay riesgo de servir una versión vieja.

Cache-Control: public, max-age=31536000, immutable

Un año de caché y ninguna revalidación. Lo de immutable es un detalle que a veces se olvida: evita que el navegador haga siquiera la petición condicional cuando el usuario pulsa recargar, que es más frecuente de lo que parece.

El HTML

Con el HTML pasa lo contrario. Tiene que reflejar el despliegue nuevo en cuanto sale, pero tampoco tiene sentido descargarlo entero cada vez si no ha cambiado nada.

Cache-Control: no-cache
ETag: "a1b2c3"

Lo que ocurre por debajo es que el navegador manda If-None-Match: "a1b2c3" y, si nada ha cambiado, el servidor contesta con un 304 Not Modified sin cuerpo. Una petición pequeña y rápida en lugar de la página entera.

Respuestas de API que pueden ir unos segundos atrasadas

Este es el caso que menos gente conoce y el que más alegrías da. Sirve para cualquier endpoint donde un desfase de un minuto no importe: un listado de productos, un contador de visitas, el tiempo que hace.

Cache-Control: public, max-age=60, stale-while-revalidate=600

Durante los primeros 60 segundos la respuesta sale de caché sin más. Entre el segundo 60 y el 660 se sigue sirviendo la copia antigua mientras se pide una nueva en segundo plano. El usuario nunca se queda esperando, y el servidor recibe como mucho una petición por minuto.

Para tenerlo a mano

Contenido Cabecera
Asset con hash public, max-age=31536000, immutable
HTML no-cache + ETag
API pública que tolera desfase public, max-age=60, stale-while-revalidate=600
Datos por usuario private, no-cache
Datos sensibles no-store

Comprobar que de verdad se está cacheando

Configurar las cabeceras es la mitad del trabajo. La otra mitad es comprobar que la CDN hace lo que crees que hace:

curl -sI https://ejemplo.com/app.4f3a9c.js | grep -i 'cache-control\|etag\|age'

Fíjate en la cabecera Age. Indica cuántos segundos lleva esa respuesta en la caché intermedia. Si siempre vale 0, la CDN no está guardando nada, y esto pasa más de lo que uno pensaría. En mi experiencia suele ser una cookie que se cuela en la respuesta o un Vary demasiado amplio.