Saltar al contenido
Construcción, optimización y experimentos web[email protected]
La Trastienda WebLo que pasa detrás de una web
Experimentos

Caché HTTP: qué cabeceras envía este sitio y qué cambiaríamos

Las cabeceras de caché reales de este sitio, leídas con curl: qué guarda el navegador, qué guarda Cloudflare, un error en nuestro propio generador y la configuración que proponemos.

Por Redacción de La Trastienda WebPublicado el 8 min de lectura
Archivador de madera con cajones etiquetados con relojes de distinta duración junto a una ventana de navegador

Cuando montamos este sitio dejamos escrito en un archivo de configuración que la hoja de estilos debía guardarse un año en el navegador. Diez días después de publicarlo, leímos las cabeceras que el servidor envía de verdad. La hoja de estilos se guarda doce horas, el HTML no lleva ninguna instrucción de caché y las imágenes reciben cuatro horas que no pusimos nosotros. Este artículo cuenta qué encontramos, cómo se lee cada cabecera y qué vamos a cambiar.

Lo que se midió y cómo

El 30 de octubre de 2026, a partir de las 07:20 UTC, se pidieron con curl -I cuatro tipos de recurso del sitio publicado: la portada, un artículo, la hoja de estilos y una ilustración SVG. Después se repitieron las mismas peticiones directamente al servidor de origen, saltándose Cloudflare, para saber qué cabecera pone cada uno. Todo desde un mismo equipo y sin navegador de por medio.

Cabeceras de caché observadas el 30/10/2026 (07:20–07:22 UTC)
RecursoCache-Control en origen (Nginx)Cache-Control tras Cloudflarecf-cache-statusValidadores
Portada y artículos (HTML)ningunoningunoDYNAMICETag y Last-Modified
/assets/estilo.css?v=b80max-age=43200 (12 h)max-age=43200MISS y, 9 s después, HITETag y Last-Modified
Ilustración portada.svgningunomax-age=14400 (4 h)HITETag y Last-Modified
sitemap.xmlningunoningunoDYNAMICETag y Last-Modified

Tres de las cuatro filas no son lo que esperábamos. Las vemos una por una.

El HTML: sin instrucciones no significa sin caché

Cloudflare no guarda HTML por defecto: decide qué almacenar por la extensión del archivo, y las páginas quedan fuera de su lista. Por eso aparecen como DYNAMIC, que significa «no apto para la caché del borde», y cada visita llega hasta nuestro servidor. Para un sitio pequeño eso no es un problema.

Lo que sí merece atención es la otra mitad: el servidor tampoco dice al navegador cuánto tiempo puede reutilizar la página. Cuando falta esa instrucción, RFC 9111 permite que el navegador calcule una caducidad por su cuenta, la llamada frescura heurística, a partir de la fecha de última modificación. La norma sugiere como valor típico el 10 % del tiempo transcurrido desde esa fecha, y un análisis del código de Chromium y WebKit publicado en 2018 encontró esa misma fracción. No hemos comprobado qué hace hoy cada navegador, así que el cálculo siguiente es un orden de magnitud, no una medición.

Cálculo con las cabeceras reales de la portada

Last-Modified: Tue, 20 Oct 2026 03:20:10 GMT
Date: Fri, 30 Oct 2026 07:20:38 GMT

Tiempo transcurrido: 10 días, 4 horas y 28 segundos, es decir, unas 244 horas. El 10 % son 24,4 horas. Un navegador que aplique esa regla podría mostrar la portada guardada durante algo más de un día sin preguntar si ha cambiado, siempre que el lector llegue navegando y no pulse recargar.

Si un artículo pasa 60 días sin tocarse y lo corregimos ese día, la misma regla da hasta 6 días durante los que un lector que ya lo hubiera visitado puede seguir viendo la versión anterior.

La paradoja es que la ventana crece cuanto más tiempo lleva una página sin cambios, que es justo cuando una corrección importa: el texto lleva semanas publicado y alguien ha avisado de un error. Para un sitio que tiene una política de correcciones, eso no encaja.

La solución que recomienda web.dev para las URL sin versión es Cache-Control: no-cache. El nombre engaña: no impide guardar la página, obliga a preguntar al servidor antes de reutilizarla. Como nuestras respuestas ya llevan ETag y Last-Modified, esa pregunta sale barata. Lo comprobamos: una petición condicional con If-None-Match devolvió 304 Not Modified con 0 bytes de cuerpo, y lo mismo con If-Modified-Since sobre la portada. El significado de cada código está en 200, 301, 404, 410 y soft 404.

Línea de tiempo: la portada se modificó el 20 de octubre; el 30 de octubre el navegador puede considerarla fresca 24,4 horas más; si pasan 60 días, la ventana llega a 6 días
Frescura heurística con la regla del 10 %: cuanto más tiempo lleva una página sin cambios, más tiempo puede reutilizarla el navegador sin preguntar.

La hoja de estilos y un archivo de configuración que no se ejecuta

En el repositorio del sitio hay un archivo _headers que pide max-age=31536000, immutable para todo lo que cuelga de /assets/. Ese formato lo entienden plataformas como Netlify o Cloudflare Pages. Nuestro sitio, en cambio, se sirve desde un servidor propio con Nginx, que no lee ese archivo. La cabecera real, max-age=43200 acompañada de Expires, la pone Nginx: el servidor de origen ya la envía antes de pasar por Cloudflare. El _headers nunca ha tenido efecto.

Es una lección general: un archivo de configuración que nadie ejecuta es peor que no tenerlo, porque da por resuelto un problema que sigue ahí. La única forma de saberlo es mirar la respuesta, no el repositorio.

Doce horas tampoco es un desastre. Lo que se pierde es el sentido de tener una URL con versión. La página pide estilo.css?v=b80, y si la hoja cambia, cambia la URL. Para un recurso así, web.dev recomienda caché de un año, y RFC 8246 añade la directiva immutable para indicar que el contenido no cambiará mientras esté fresco, de modo que el navegador ni siquiera pregunte.

El fallo que encontramos en nuestro generador

Al revisar de dónde sale b80, apareció un error nuestro. El generador calculaba la versión a partir del tamaño del archivo: 14.544 bytes escritos en base 36 dan exactamente b80. Funciona mientras cada cambio altere el tamaño, pero no siempre es así. Cambiar el color #b4583a por #a4583a deja el archivo con los mismos bytes, la URL no cambia y cualquier caché que ya tuviera la hoja sigue sirviendo la antigua. Con una caché de un año, ese error duraría un año.

El 30 de octubre lo cambiamos: la versión es ahora un resumen SHA-256 del contenido, recortado a 10 caracteres. Cualquier cambio en la hoja, aunque no altere su tamaño, produce una URL nueva. Es un requisito previo para poder subir la caché a un año sin miedo.

Las imágenes: cuatro horas que puso Cloudflare

Nginx no envía Cache-Control para los SVG: la regla que da 12 horas a la hoja de estilos no se aplica a las imágenes. La cabecera de 4 horas que ve el navegador la añade Cloudflare. Su documentación lo explica: si el origen no envía Cache-Control ni Expires, o envía un valor menor que el ajuste Browser Cache TTL de la zona, Cloudflare pone el suyo. Las SVG sí están en la lista de extensiones que Cloudflare guarda, por eso la respuesta llegó como HIT con una edad de 32 segundos: otra petición la había llevado ya a la caché del borde.

Para las ilustraciones de este sitio, 4 horas es poco pero no es grave. El problema es que no lo decidimos nosotros. Si mañana cambia ese ajuste de la zona, cambia el comportamiento sin que nadie toque el servidor.

Lo que la compresión ya hace bien

No todo eran sorpresas. La misma ronda de peticiones confirmó que el texto viaja comprimido.

Bytes descargados con y sin compresión (30/10/2026)
RecursoSin comprimirComprimidoFormatoAhorro
Portada (HTML)16.4044.355Brotli73,5 %
Hoja de estilos14.5443.717gzip74,4 %

Un detalle: el HTML llega con Brotli y la CSS con gzip. En nuestras peticiones, el recurso servido desde la caché del borde fue el que llegó en gzip, pero no hemos investigado la causa, y con 3,7 KB el margen de mejora es pequeño. Lo dejamos anotado.

La configuración que proponemos

Una política por tipo de recurso, según tenga o no versión en la URL:

Política de caché propuesta para La Trastienda Web
Tipo¿La URL cambia si cambia el contenido?CabeceraPor qué
HTML, sitemap, feedNono-cacheLas correcciones se ven en la siguiente visita; la revalidación cuesta un 304 sin cuerpo.
CSS con ?v= por contenidoSípublic, max-age=31536000, immutableNinguna petición en visitas repetidas durante un año.
Ilustraciones SVG y PNGNopublic, max-age=86400Cambian rara vez; un día es un retraso aceptable si alguna se corrige.

En Nginx podría escribirse así. Es un fragmento ilustrativo: no estaba aplicado en la fecha de este artículo y hay que adaptarlo a la configuración existente.

location /assets/ {
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Strict-Transport-Security "max-age=31536000" always;
}
location /img/ {
    add_header Cache-Control "public, max-age=86400";
    add_header Strict-Transport-Security "max-age=31536000" always;
}
location / {
    add_header Cache-Control "no-cache";
    add_header Strict-Transport-Security "max-age=31536000" always;
    try_files $uri $uri/index.html =404;
}

Cómo hacer la misma revisión en tu sitio

  • Pide cada tipo de recurso con curl -I https://tu-dominio/ruta y apunta Cache-Control, Expires, ETag y Last-Modified.
  • Si usas una CDN, repite la petición contra el origen. Es la única forma de saber quién añade cada cabecera.
  • Pide dos veces el mismo recurso estático: si pasa de MISS a HIT, la CDN lo está guardando.
  • Prueba una petición condicional copiando el ETag: curl -I -H 'If-None-Match: "valor"' URL. Debe responder 304.
  • Revisa cómo se genera la versión de tus archivos estáticos: debe depender del contenido, no de la fecha ni del tamaño.
  • Busca en tu repositorio archivos de configuración de plataformas que no usas (_headers, .htaccess, vercel.json) y comprueba si alguno se aplica de verdad.

Antes de cambiar nada, guarda la tabla de cabeceras con su fecha, como se explica en Cómo montar un experimento web; después del cambio, repite exactamente las mismas peticiones. La caché no aparece en la puntuación de PageSpeed de una primera visita, así que si sólo miras esa cifra no verás la diferencia. Las cifras de peso de cada página están en la bitácora del sitio, y lo que pasa con el presupuesto cuando el navegador tiene que volver a pedirlo todo se calcula en Presupuesto de rendimiento.

Cuándo no vale la pena tocar esto

Si tu sitio se publica en una plataforma gestionada que ya envía cabeceras razonables, o si casi todas tus visitas son de una sola página, el beneficio de afinar la caché es pequeño: la primera visita descarga lo mismo con cualquier configuración. Lo que sí conviene en cualquier sitio que corrija sus textos es evitar que el HTML quede sin instrucciones de caché. Cuando apliquemos la configuración propuesta, repetiremos estas mediciones y las añadiremos a este artículo con su fecha.

Cómo se ha elaborado este artículo

Las cabeceras de la tabla principal se leyeron con curl el 30 de octubre de 2026, entre las 07:20 y las 07:22 UTC, desde un único equipo, contra el sitio publicado y contra el servidor de origen. Son una foto de ese momento, no un seguimiento continuo. Las reglas de caché proceden de RFC 9111, RFC 8246, la guía de caché HTTP de web.dev y la documentación de Cloudflare; el cálculo de frescura heurística es una aplicación de la fracción del 10 % que sugiere RFC 9111, no una medición del comportamiento de cada navegador. La configuración de Nginx propuesta no estaba aplicada en la fecha de publicación.

Fuentes consultadas

  1. IETF: RFC 9111: HTTP Caching
  2. IETF: RFC 8246: HTTP Immutable Responses
  3. web.dev: Prevent unnecessary network requests with the HTTP Cache
  4. Cloudflare Docs: Edge and Browser Cache TTL
  5. Cloudflare Docs: Default cache behavior
  6. nginx.org: Module ngx_http_headers_module

¿Has visto un dato erróneo o desactualizado? Escribe a [email protected] indicando la URL. Las correcciones se aplican según la política editorial.