// Engineering Log

HTTPS y servidor web en la práctica: Parte 4 — Almacenamiento en caché HTTP y CDN: Cache-Control, ETag, proxy_cache y purga de caché

Publicado el 06.11.2026 · Autor:

// Ruta rápida

Este artículo pertenece al tema Servidores e infraestructura.

El almacenamiento en caché — la forma más barata de acelerar un sitio y reducir la carga del servidor: una respuesta que no necesita volverse a generar ni transmitir llega al instante. Pero la caché tiene una cara B — los usuarios ven estilos antiguos después del despliegue, un CDN sirve la página personal de un visitante a otro, y los cambios en el sitio «no se aplican». Casi todos esos problemas se resuelven con los encabezados Cache-Control correctos y entendiendo dónde se almacena exactamente la copia. Un resumen breve de los encabezados de caché está en el artículo «Encabezados HTTP»; aquí — cómo usarlos.

Dónde se almacenan las copias

Entre el usuario y la aplicación suele haber varias cachés:

  1. Caché del navegador — personal, solo para un usuario.
  2. CDN (Cloudflare, CDN en la nube y CDN rusos) — caché compartida en servidores por todo el mundo; una copia se sirve a todos los visitantes.
  3. Proxy inverso — Nginx con proxy_cache, Varnish — caché compartida cerca de la aplicación.
  4. Caché dentro de la aplicación — Redis, memcached, caché de páginas en CMS. No tiene relación con los encabezados HTTP.

Las reglas para los tres primeros las establece el servidor en los encabezados de respuesta. Las cachés compartidas (CDN, proxy) por defecto son más prudentes que el navegador, y los CDN tienen ajustes propios que pueden sobrescribir los encabezados.

Cómo decide la caché qué hacer

La copia en caché puede ser fresca o obsoleta. Una copia fresca se entrega sin consultar al servidor. Una copia obsoleta se verifica primero — con una petición condicional If-None-Match (por ETag) o If-Modified-Since (por Last-Modified). Si el recurso no cambió, el servidor responde con un breve 304 Not Modified sin cuerpo, y la caché renueva la copia.

Cuánto tiempo permanece fresca lo define Cache-Control: max-age en segundos. Si no hay ni Cache-Control ni Expires, las cachés pueden estimar el tiempo por su cuenta — normalmente 10 % del tiempo desde Last-Modified. Un archivo modificado hace un año puede ser considerado fresco por el navegador más de un mes. Por eso siempre hacen falta encabezados explícitos.

Directivas Cache-Control

DirectivaSignificado
max-age=Nfresco N segundos
s-maxage=Nigual, pero solo para cachés compartidas; tiene prioridad sobre max-age
no-cachese puede almacenar, pero antes de usarlo comprobar con el servidor
no-storeno almacenar en ningún sitio
privatesolo el navegador; las cachés compartidas no pueden
publicse puede almacenar en cachés compartidas, incluso si la petición tenía Authorization
must-revalidateuna copia obsoleta no puede entregarse sin verificación, incluso si el servidor no está disponible
immutableel recurso no va a cambiar — no verificar ni siquiera al recargar la página
stale-while-revalidate=NN segundos tras quedar obsoleta, entregar la copia antigua mientras se actualiza en segundo plano
stale-if-error=NN segundos entregar la copia antigua si el servidor responde con error

Para los CDN existe un encabezado separado CDN-Cache-Control (RFC 9213): actúa solo sobre el CDN y no es visible para el navegador. Así se puede mantener una página en el CDN una hora y en el navegador — sin caché.

Estrategias comunes

Estáticos con hash en el nombre (app.3f9a1c.js, style.8b2e.css — así empaquetan Vite, webpack, Hugo Pipes): al cambiar el archivo cambia el nombre, por eso la copia antigua se puede mantener para siempre.

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

Páginas HTML: los enlaces a las nuevas versiones de los recursos estáticos están en el HTML, por eso el HTML debe actualizarse inmediatamente.

Cache-Control: no-cache
ETag: "5f2a-18c4"

El navegador guarda la página, pero la comprueba cada vez: si no cambió — 304 sin cuerpo.

Estáticos sin hash (imágenes desde un CMS, favicon.ico, robots.txt): tiempo corto y verificación.

Cache-Control: public, max-age=3600, stale-while-revalidate=86400

Páginas personales y respuestas API con datos del usuario:

Cache-Control: private, no-store

Datos públicos de API que cambian con frecuencia (cotizaciones, existencias):

Cache-Control: public, max-age=10, stale-while-revalidate=30

Configuración en Nginx

Encabezados para estáticos y HTML:

nginx
location ~* \.(?:js|css|woff2)$ {
    # solo para archivos con hash en el nombre
    add_header Cache-Control "public, max-age=31536000, immutable" always;
}

location ~* \.(?:png|jpe?g|webp|avif|svg|ico)$ {
    add_header Cache-Control "public, max-age=86400" always;
}

location / {
    add_header Cache-Control "no-cache" always;
    try_files $uri $uri/ /index.html;
}

Nginx por sí solo sirve ETag y Last-Modified para archivos estáticos y responde 304 a peticiones condicionales. Para respuestas de la aplicación, ETag debe ponerlo la propia aplicación.

Caché en Nginx: proxy_cache

Nginx puede almacenar respuestas de la aplicación y servirlas sin consultar al backend:

nginx
proxy_cache_path /var/cache/nginx/app levels=1:2 keys_zone=app:50m
                 max_size=2g inactive=7d use_temp_path=off;

server {
    location / {
        proxy_pass http://127.0.0.1:8080;

        proxy_cache app;
        proxy_cache_key "$scheme$host$request_uri";
        proxy_cache_valid 200 301 10m;
        proxy_cache_valid 404 1m;

        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_background_update on;
        proxy_cache_lock on;

        # no cachear para usuarios autenticados
        proxy_cache_bypass $cookie_sessionid;
        proxy_no_cache     $cookie_sessionid;

        add_header X-Cache-Status $upstream_cache_status always;
    }
}
  • proxy_cache_use_stale — en caso de error de la aplicación servir una copia obsoleta: el sitio sigue disponible mientras la aplicación se reinicia;
  • proxy_cache_background_update y updating — actualizar la copia obsoleta en segundo plano, sirviendo la antigua (análogo a stale-while-revalidate);
  • proxy_cache_lock — ante un fallo de caché hacia la aplicación va una sola petición, las demás esperan su resultado. Protege contra avalanchas de peticiones cuando una página popular sale de la caché;
  • X-Cache-Status mostrará HIT, MISS, EXPIRED, STALE, UPDATING o BYPASS.

Nginx respeta los encabezados de la aplicación: no cachea respuestas con Set-Cookie, con Cache-Control: private, no-store, no-cache, y el tiempo de Cache-Control: max-age lo sitúa por encima de proxy_cache_valid.

Microcaché — caché de un segundo (proxy_cache_valid 200 1s) para páginas públicas bajo carga: con mil peticiones por segundo, al backend llega una, y los datos se desactualizan como máximo un segundo.

CDN

Qué es importante saber sobre los CDN:

  • Los CDN cachean según sus propias reglas. Cloudflare por defecto cachea solo estáticos por extensión de archivo (.js, .css, imágenes, fuentes), pero no HTML. Para cachear páginas hay que crear una regla de caché (Cache Rules). Otros CDN pueden, en cambio, cachear todo lo que permitan los encabezados;
  • la clave de la caché — normalmente host, ruta y query string. Parámetros como utm_source, fbclid, yclid crean miles de copias de la misma página — en la configuración del CDN conviene excluirlos de la clave;
  • cookies. Si la respuesta depende de cookies y el CDN la cachea por URL, un visitante puede recibir la página de otro. Respuestas personales — solo private o evitar caché si existe la cookie de sesión;
  • el estado de la caché se ve en los encabezados: en Cloudflare — CF-Cache-Status (HIT, MISS, EXPIRED, DYNAMIC — no cacheada por reglas, BYPASS — evitada por encabezados de respuesta), en otros — X-Cache, y el encabezado Age muestra la antigüedad de la copia.

Comprobación:

bash
curl -sI https://example.ru/assets/app.3f9a1c.js | grep -iE 'cache-control|cf-cache-status|x-cache|age|etag'

Repite la petición: con la configuración correcta la segunda vez será HIT y Age será distinto de cero.

Purgado de caché

  • Nombres de archivos con hash — la forma principal para estáticos: no hace falta purgar nada, la nueva versión tiene nueva dirección.
  • Parámetro de versión (style.css?v=42) — funciona si el CDN tiene en cuenta el query string en la clave; es peor que el hash porque la versión se cambia manualmente.
  • Purge en el CDN — vía panel o API, por URLs o por completo. Tras un despliegue es útil purgar el HTML; purgar «todo» después de cada despliegue elimina el beneficio del caché estático.
  • Nginx proxy_cache — borrar el directorio de caché o archivos individuales; en la versión gratuita de Nginx no hay comando para purgar por URL, eso lo proporcionan módulos terceros o Nginx Plus.
  • No se puede forzar el vaciado del caché del navegador desde el servidor. Si enviaste un archivo con max-age=31536000 sin hash en el nombre, los usuarios verán la versión antigua hasta que caduque el plazo o limpien la caché. El encabezado de respuesta Clear-Site-Data: "cache" borra la caché del sitio en el navegador, pero solo cuando el navegador reciba esa respuesta — y la página que está en caché el navegador no la solicita al servidor. Por eso un tiempo largo es solo para archivos con hash.

Errores típicos

  • No hay Cache-Control en absoluto — los navegadores hacen caché por heurística, y después del despliegue parte de los usuarios ve la maquetación antigua.
  • max-age por un año para style.css sin hash en el nombre — los cambios no llegan a los visitantes que regresan.
  • HTML cacheado durante largo tiempo — la página referencia archivos remotos de una build antigua, el sitio «se rompe» para parte de los usuarios.
  • Página personal sin private detrás de un CDN que cachea HTML — fuga de datos entre usuarios.
  • no-cache en lugar de no-store donde los datos no deben almacenarse en disco en absoluto.
  • Purgar todo el caché del CDN en cada despliegue — tras el despliegue todas las peticiones van al servidor a la vez.
  • Depurar «por qué no se actualizó» con la caché del navegador activada — comprueba con curl o con Disable cache en DevTools.

// Tarea parecida

Si estás resolviendo algo parecido

Este artículo pertenece a uno de los temas principales de trabajo. Puedes seguir leyendo sobre el tema, ir a la página principal para entender a qué me dedico o abrir directamente los servicios.

Tema del artículo

Servidores e infraestructura

VPS, Linux, stack web, migraciones, hosting, bases de datos y operacion base.

Tareas frecuentes de este tema

  • Migrar un sitio o servicio a un nuevo servidor
  • Configurar Linux, Nginx, base de datos y copias de seguridad
  • Entender por que el sistema funciona de forma inestable

// Siguiente paso

Si necesitas ayuda con este tema y no solo otro artículo, es mejor ir directo a la página del servicio. La página principal y la selección de materiales quedan como rutas secundarias.

Abrir servicios

// Reviews

Reseñas relacionadas

apande

apande

Configuración de Nginx y OpenCart

07.09.2024 · ★ 5/5

Un comprador muy poderoso
kireevk

kireevk

Diagnóstico de Nginx Proxy Manager en un contenedor Docker y solución del problema

15.03.2024 · ★ 5/5

Comprador acostumbrado

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

O deje su solicitud aquí:

Confirme que no es un bot.

Escribir y recibir una respuesta rápida