Cada recurso que sirve un servidor (HTML, CSS, JavaScript, imágenes) puede ir acompañado de cabeceras HTTP que le dicen al navegador, y a cualquier caché intermedia entre el servidor y el usuario, cuánto tiempo puede reutilizar ese recurso sin volver a pedirlo. Configurar bien estas cabeceras es una de las optimizaciones de rendimiento con mejor relación entre esfuerzo y resultado: no requiere tocar el código de la página, solo la configuración del servidor, y su efecto en la velocidad percibida por usuarios recurrentes es inmediato.
Cache-Control: la cabecera que sustituyó a Expires
Cache-Control es la cabecera moderna de control de caché, con varias directivas combinables. max-age especifica en segundos cuánto tiempo puede considerarse "fresco" el recurso sin volver a consultarlo; public permite que cualquier caché intermedia (un CDN, un proxy) lo almacene, no solo el navegador del propio usuario; private restringe el almacenamiento al navegador del usuario, apropiado para contenido personalizado; e immutable le dice al navegador que ese recurso nunca va a cambiar de contenido bajo esa misma URL, evitando incluso la comprobación de si sigue vigente:
Header set Cache-Control "public, max-age=31536000, immutable"
La cabecera antigua, Expires, cumplía una función similar pero con una fecha absoluta en vez de una duración relativa, lo que la hacía más propensa a errores (una fecha mal calculada, un reloj de servidor desincronizado); Cache-Control con max-age la ha sustituido en la práctica totalidad de configuraciones modernas, aunque muchos servidores siguen enviando ambas por compatibilidad.
ETag y Last-Modified: validación condicional
Cuando el tiempo de max-age expira, el navegador no necesariamente vuelve a descargar el recurso entero: puede hacer una petición condicional preguntando "¿ha cambiado esto desde la última vez?", usando el ETag (un identificador único que cambia si el contenido cambia) o la fecha Last-Modified. Si el servidor confirma que no ha cambiado, responde con un código 304 (Not Modified) sin cuerpo, ahorrando la descarga completa; si ha cambiado, responde con el recurso nuevo y un ETag actualizado.
GET /style.css HTTP/1.1
If-None-Match: "a1b2c3d4"
HTTP/1.1 304 Not Modified
ETag: "a1b2c3d4"
El patrón que resuelve el dilema entre caché agresiva y contenido actualizado
El riesgo de una caché muy larga (un año, como en el ejemplo anterior) es que, si el contenido de ese archivo cambia (una actualización del CSS, por ejemplo), los usuarios con la versión anterior en caché no verán el cambio hasta que expire ese año completo. La solución estándar, y la que ya usa este mismo sitio en sus propios archivos CSS y JS, es el versionado por parámetro de consulta (style.css?v=139): cada vez que el archivo cambia de contenido, cambia también su URL (al cambiar el número de versión), así que la caché antigua nunca se reutiliza para el contenido nuevo, y se puede cachear de forma extremadamente agresiva (un año, immutable) sin ningún riesgo de servir contenido desactualizado.
Qué cachear de forma agresiva y qué no cachear nunca
Los recursos estáticos con nombre versionado (CSS, JS, imágenes con hash o parámetro de versión en la URL) son los candidatos ideales para caché de un año con immutable. El HTML de las páginas, en cambio, normalmente no debe cachearse de forma agresiva (o debe usar no-cache, que exige siempre una validación condicional antes de reutilizar la copia local) porque su contenido cambia con más frecuencia y sin un cambio de URL asociado; cachearlo igual de agresivamente que un CSS versionado significaría que los usuarios verían contenido desactualizado durante días o semanas sin ningún aviso.
CDN: qué resuelve exactamente y por qué reduce la latencia
Un CDN (red de distribución de contenido) mantiene copias de los recursos estáticos de un sitio en servidores distribuidos geográficamente, de forma que un usuario en Australia no tiene que esperar a que los datos viajen físicamente desde un servidor de origen en Europa, sino que los recibe del nodo del CDN más cercano a su ubicación real. El efecto es una reducción directa de la latencia de red (el tiempo que tarda un paquete de datos en ir y volver), que se nota especialmente en Time to First Byte y, por tanto, en el LCP de la página, como se explica en la guía de Core Web Vitals.
Compresión: Brotli frente a gzip
Además de la caché, la compresión de texto (HTML, CSS, JavaScript, JSON) antes de enviarlo reduce directamente el peso transferido sin perder ni un byte de información (es compresión sin pérdida). gzip es el estándar histórico, soportado universalmente; Brotli, desarrollado por Google, comprime entre un 15% y un 25% mejor que gzip en la mayoría de casos y hoy tiene soporte prácticamente universal en navegadores modernos, así que es la opción preferente cuando el servidor lo soporta, con gzip como alternativa automática para el resto de casos.
Errores de configuración de caché más comunes en auditorías reales
El más habitual es no cachear en absoluto recursos estáticos que nunca cambian de contenido bajo su URL (un logo, una fuente web), perdiendo una optimización gratuita. El segundo es cachear de forma agresiva el HTML de páginas que cambian con frecuencia, generando el problema de contenido desactualizado ya descrito. El tercero, más sutil, es servir la misma cabecera de caché a través de un CDN que a través del servidor de origen sin tener en cuenta que muchos CDN tienen su propia capa de caché adicional (con su propio tiempo de vida, configurable de forma independiente), lo que puede generar comportamientos inesperados si no se entienden ambas capas por separado.
Preguntas frecuentes
¿La caché del navegador afecta al SEO directamente?
No es un factor de ranking en sí misma, pero mejora directamente la velocidad de carga para usuarios recurrentes, lo que sí influye en Core Web Vitals y en la experiencia general que Google evalúa indirectamente a través de esas métricas.
¿Debo usar un CDN aunque mi sitio reciba tráfico solo de un país?
Sigue aportando valor incluso dentro de un mismo país, porque distribuye la carga y reduce la latencia también a nivel regional, además de añadir una capa de protección frente a picos de tráfico inesperados que el servidor de origen no tendría que absorber directamente.
¿Qué pasa si cacheo agresivamente un archivo y necesito corregir un error urgente en él?
Si el archivo usa versionado por parámetro de consulta, basta con subir la corrección con un número de versión nuevo: los usuarios descargarán inmediatamente la versión corregida porque la URL ha cambiado. Sin versionado, hay que esperar a que expire la caché o purgarla manualmente en el CDN, si lo hay.
¿Brotli sustituye completamente a gzip?
En la práctica conviven: los servidores modernos ofrecen Brotli al navegador que lo soporta y recurren a gzip automáticamente para el resto, mediante negociación de contenido basada en la cabecera Accept-Encoding que envía cada navegador.
¿Cuánto tiempo debo cachear el HTML de mis páginas?
Para contenido que cambia con el mini-CMS o similar, lo habitual es no cachear en el navegador (no-cache) o usar tiempos muy cortos, dejando que sea la caché de un CDN o de un sistema de caché de servidor (con capacidad de purga inmediata al publicar un cambio) la que absorba la mayor parte del tráfico repetido.