Las 5 capas de la caché web: del navegador a la base de datos | Backend Survivor

Las 5 capas de la caché web: del navegador a la base de datos

Cuando un usuario hace clic en un enlace, su petición no llega directa a tu servidor. Antes de tocar tu aplicación, atraviesa una serie de capas de caché que —bien configuradas— hacen que todo parezca instantáneo sin quemar CPU, base de datos ni red. Es lo que se llama defensa en profundidad: la caché va por capas, como los ogros.

En el vídeo de Escuela Drupal se recorre ese camino de fuera hacia dentro. Vamos a ver cada capa.

Las cinco capas, de un vistazo

  1. APCu — caché a nivel de proceso (la más profunda y rápida)
  2. Redis o backend compartido — caché key-value compartida entre procesos
  3. Caché HTTP interna — la respuesta HTML completa a nivel de aplicación
  4. CDN o reverse proxy — la caché en el borde (edge)
  5. Caché del navegador — la gran olvidada, junto con la CDN

La más rápida es APCu, porque vive en el propio proceso. Las demás son “un poquito más lentas”, aunque a nivel de milisegundos que ni notamos como humanos. La diferencia está en la capacidad y en la compartición: cuanto más externa es la capa, más se comparte y más contenido puede guardar.

Capa 1: APCu, la caché de proceso

APCu es una caché a nivel de lenguaje o de proceso, nativa de PHP (aunque otros lenguajes tienen sus equivalentes). Es una memoria extremadamente rápida pero muy pequeña: típicamente 16, 24 o como mucho 32 MB.

Dos características clave:

  • Es volátil: no se comparte entre procesos distintos. Cada proceso PHP tiene su propia memoria.
  • Es diminuta: no cabe mucha información, así que no se guarda ahí contenido “útil” de forma persistente, sino datos que se reutilizan muchas veces durante la generación de la página.

En Drupal, por ejemplo, APCu almacena la configuración y las traducciones para que su acceso sea prácticamente instantáneo — pueden ser miles de entradas.

Capa 2: Redis, el backend compartido

La siguiente barrera es Redis (o Valkey, Dragonfly o cualquier fork que te guste). Es una base de datos key-value muy rápida y persistente, compartida entre todos los procesos de la aplicación.

Su función principal es ser la barrera entre tu base de datos y la sobrecarga: la idea es quitarle carga al origen. Todos los procesos, antes de consultar la base de datos, deberían mirar primero en Redis.

Casos de uso típicos: backend de sesiones o cachear trocitos de una página — un menú, el footer, una sección concreta. Ojo: no el HTML completo, sino fragmentos.

Un detalle importante: hay un pequeño retardo de I/O de red (de microsegundos a milisegundos). En una web mal hecha que hace miles de peticiones a la caché para mostrar una sola página, ese retardo acaba notándose.

Hit vs. miss y estrategias de escritura

  • Hit: el dato está en caché y se sirve sin tocar el origen.
  • Miss: no está, hay que generarlo, almacenarlo y servir.

La estrategia más común es que tu framework gestione la caché y tú te olvides. Pero en sistemas de mucha carga se usan otras:

  • Escribir en caché y en base de datos al mismo tiempo (write-through).
  • Actualizar primero la caché y después la base de datos (write-behind), para servir la versión nueva lo antes posible. Puede traer problemitas de consistencia, pero es una estrategia válida.

Capa 3: la caché HTTP interna

Esta es la caché a nivel de aplicación: cuando ya tienes todo el HTML generado, guardas la respuesta completa en Redis o en tu sistema de almacenamiento. A diferencia de la capa anterior, aquí no se cachean trocitos, sino la respuesta entera.

El flujo es sencillo:

  1. Llega una petición: “¿Tienes esta URL cacheada?”
  2. Hit → se sirve la respuesta directamente. Todos felices: el servidor no se sobrecarga y el usuario accede al instante.
  3. Miss → se ejecuta toda la aplicación, se genera el HTML, se guarda en caché y se devuelve.

La idea es no sobrecargar el servidor y que todo vaya lo más rápido posible.

Capa 4: CDN o reverse proxy, la caché en el borde

Junto con la del navegador, esta es la gran olvidada por los desarrolladores de backend (el autor se incluye). Aquí hablamos de Cloudflare, Fastly, Akamai, o de montar tu propio Varnish o Nginx.

La ventaja: la petición del usuario ni siquiera llega a tu servidor. Si tu aplicación está en España y alguien en Argentina, Colombia o México hace clic, no tiene que viajar hasta España: la CDN le sirve la versión cacheada desde un punto cercano. La latencia se vuelve mínima y la carga instantánea.

Y esto tiene un componente de dinero: la CDN es mucho más barata que el servidor. Cuantas menos consultas lleguen a tu origen, menos gastas. Esa es la parte en la que configurar bien la caché te ahorra bastante dinerito.

Los headers que importan

  • Cache-Control: public siempre, salvo en respuestas de usuarios autenticados, que deben ir private — y además con no-cache para que no lo cachee el navegador.
  • no-store: crítico por seguridad. Piensa en un panel administrativo: cierras sesión y, si le das “atrás” en el navegador, la caché puede mostrar información sensible. no-store evita eso.
  • s-max-age=3600: tiempo de vida para la CDN (una hora).
  • max-age=300: tiempo de vida para el navegador (cinco minutos).
  • Si solo indicas max-age sin s-max-age, la CDN usará un valor por defecto. Indica el correspondiente para cada capa.

stale-while-revalidate

Una estrategia muy interesante: cuando la caché caduca, la CDN tiene permiso para seguir sirviendo el contenido obsoleto mientras hace una petición al origen en segundo plano, refresca la caché y la vuelve a almacenar. Así reduces muchísimo la cantidad de peticiones que llegan a tu servidor.

Capa 5: la caché del navegador

Aquí está el usuario haciendo clics para ver gatitos. La caché del navegador se gestiona con las mismas directivas: max-age, no-cache, no-store. Recuerda el ejemplo del panel administrativo: no-store no es solo una optimización, es un problema de seguridad.

ETag: la validación por hash

La ETag (Entity Tag) es un hash que genera el servidor y le dice al navegador: “este es el hash de validación de este asset, guárdalo durante un mes o un año”. El flujo:

  1. El navegador tiene el asset en caché y, cuando expira, pregunta: “¿Sigue siendo válida esta ETag?”
  2. El servidor responde 304 Not Modified → el navegador sigue sirviendo su copia y no consume tráfico de red.
  3. Si cambió en el servidor → se devuelve la copia nueva.

La ETag débil (empieza por W/) es prácticamente igual, pero no valida que el archivo sea byte a byte exacto, sino que el contenido sea el mismo.

Chrome por defecto cachea los assets (JS, CSS, imágenes) durante un año aproximadamente. Aunque normalmente se controla a nivel de servidor, desde el backend también puedes indicar las cabeceras de caché que quieras.

El flujo completo

De fuera hacia dentro: navegador → CDN → caché HTTP interna → Redis → APCu → base de datos.

  • Capas externas: navegador y CDN. Son responsabilidad del equipo de DevOps/SRE/infraestructura, pero como backend deberías preocuparte por ellas también.
  • Capas internas: la caché HTTP, Redis y APCu, que viven en tu aplicación.

Todos los implicados deberían asegurarse de que la aplicación devuelva las cabeceras correctas y de que luego se gestionen bien. Al final todo importa con el tema del dinerito.

Caché fría vs. caché caliente

  • Caché fría (sin nada cacheado): la petición llega hasta los datos. Latencia alta, mucho costo de CPU, base de datos y red. Regenerar una request completa es caro.
  • Caché caliente: todo está cacheado. Latencia bajita, el usuario ve todo rapidísimo y el coste es casi cero (no literalmente, pero muy bajito).

De ahí nace un problema interesante: el cache stampede — cuando la caché caduca y todos los usuarios golpean el origen a la vez. Si te interesa, es material para un vídeo aparte.

Invalidación: la parte difícil

Invalidar caché es, como dice la famosa frase, una de las dos cosas difíciles en desarrollo (la otra es nombrar cosas). Métodos habituales:

  • TTL (Time To Live): la caché expira al llegar a un límite temporal.
  • Invalidación por tags: invalidar todo lo que tenga una determinada etiqueta. A veces se combina con caché permanente, que va llenando el almacenamiento y puede ser un problema con el tiempo.
  • Purgar la CDN / reverse proxy.
  • stale-while-revalidate: servir contenido obsoleto mientras se actualiza en segundo plano.

Conclusiones y consejos

  • Son cinco capas, cada una con un tradeoff entre velocidad, capacidad y compartición.
  • La invalidación es un problema real y causa más dolores de cabeza de los que parece.
  • Los headers son muy importantes: configúralos bien y ahorras dinero en servidores.
  • Antes de optimizar como loco, saber qué es lo que hay que optimizar y dónde están los problemas de verdad.
  • Nunca cachear agresivamente: invalida con precisión y mide bien lo que haces.
  • No te preocupes por el rendimiento desde el inicio: primero haz que funcione, después que funcione bien, y solo entonces optimiza. No empieces la casa por el tejado (ni optimices el login mientras lo estás construyendo).
  • La regla de oro: la caché escala o reduce la latencia de red, pero nunca oculta una consulta SQL lenta ni una aplicación lenta. Una query de 5 segundos sin caché está rota.
  • Y recuerda: el cold cache lo paga el primer usuario. Ahí está el cache stampede.