wandres.dev
NETWORK II · Filtrar, bloquear y simular

Desactivar la caché con criterio, y por qué te engaña

Qué hace exactamente la casilla, las cuatro capas de caché que existen y cuáles ignora, la diferencia con las recargas forzadas, y cuándo no usarla.

⏱ 16 min

La casilla de desactivar la caché es probablemente el ajuste más usado de todas las DevTools y uno de los peor entendidos. No desactiva toda la caché: desactiva una de las cuatro que hay, mientras el panel esté abierto, y solo para las peticiones de esa pestaña. Las otras tres siguen funcionando, y una de ellas es exactamente la que causa el problema que la gente intenta resolver marcando la casilla.

🎯 Al terminar esta lección sabrás
  • Explicar qué hace exactamente la casilla y qué capas de caché no toca.
  • Distinguir la recarga normal, la forzada y la que vacía la caché.
  • Diagnosticar el caso de los cambios que no aparecen pese a recargar.
  • Decidir cuándo medir con caché y cuándo sin ella.

Las cuatro capas

Cuando el navegador necesita un recurso, hay cuatro sitios donde puede encontrarlo antes de salir a la red.

La caché de memoria. Recursos ya cargados en esta misma sesión de la pestaña, guardados en memoria del proceso de renderizado. Es la más rápida y la más volátil: desaparece al cerrar la pestaña. En el panel aparece indicada como servida desde memoria.

La caché de disco. La caché HTTP tradicional, gobernada por las cabeceras de la respuesta. Persiste entre sesiones y entre reinicios.

La caché del service worker. Un almacén controlado por código, que el worker consulta o no según su propia lógica. No obedece a ninguna cabecera HTTP y no se ve afectada por nada de lo que hagas en el panel de red.

La caché de retroceso. Una copia del documento entero, con su JavaScript en memoria y su estado intacto, que permite volver atrás instantáneamente sin ninguna petición.

La casilla de desactivar la caché actúa sobre las dos primeras. Añade cabeceras a cada petición para forzar la revalidación y hace que el navegador ignore lo que tenga guardado.

Sobre la tercera y la cuarta no hace absolutamente nada.

⚠️
Cuidado

La casilla solo tiene efecto mientras las DevTools están abiertas. En cuanto cierras el panel, la caché vuelve a funcionar con normalidad. Eso no es un capricho: el ajuste vive en el frontend de las DevTools y se comunica al navegador por el canal, así que al cerrarse el canal deja de aplicarse.

Las tres recargas

Acción Qué hace Cuándo usarla
Recarga normal Usa la caché según las cabeceras; puede revalidar Uso corriente
Recarga forzada Ignora la caché para el documento y sus subrecursos, revalidando todo Cuando sospechas de la caché
Vaciar caché y recargar Borra la caché del sitio y recarga desde cero Para medir la primera visita

La recarga forzada se hace con Cmd+Shift+R o Ctrl+Shift+R. La tercera opción aparece manteniendo pulsado el botón de recargar con las DevTools abiertas.

Ninguna de las tres desregistra el service worker ni vacía su caché. La recarga forzada hace que el navegador pida los recursos saltándose el worker en el caso del documento principal, pero el worker sigue registrado y volverá a interceptar en la siguiente carga normal.

Por qué tus cambios no aparecen

El problema más frecuente del desarrollo web, con sus cinco causas ordenadas por probabilidad real.

Uno: un service worker sirve la versión vieja. Es la causa número uno y la que más resiste. Ninguna recarga la resuelve, la casilla de desactivar la caché no la toca, y el panel de red muestra la petición como si hubiera ocurrido. La solución es la casilla de saltarse el worker para las peticiones de red, que está en la sección de service workers del panel de aplicación, o desregistrarlo directamente.

Dos: un intermediario cachea. Un CDN, un proxy corporativo o un balanceador puede estar sirviendo una copia antigua. Se distingue porque el problema también aparece en incógnito y en otros navegadores. Las cabeceras de la respuesta suelen delatarlo con alguna indicación de acierto de caché.

Tres: un override local. Ya tratado en su nivel. Persiste indefinidamente y es la explicación más embarazosa de todas.

Cuatro: la caché de retroceso. Si has llegado a la página pulsando atrás, es posible que el documento se haya restaurado entero desde memoria sin ejecutar nada. No hay peticiones porque no hay carga.

Cinco: la caché HTTP normal. La causa que todo el mundo asume primero y que la casilla sí resuelve.

El orden de esa lista es el orden en que hay que comprobarlas, y no coincide con el orden en que la gente las comprueba.

Cuándo no desactivar la caché

Aquí está el engaño del título. Trabajar permanentemente con la caché desactivada tiene tres consecuencias que distorsionan lo que ves.

Las mediciones son del peor caso. Ningún usuario recurrente navega sin caché. Un waterfall medido así corresponde a la primera visita de un usuario nuevo, que es uno de los cinco escenarios posibles y probablemente no el mayoritario.

Los bugs de caché se vuelven invisibles. Si tu aplicación tiene un problema con una respuesta cacheada —una API que devuelve datos viejos porque alguien puso una política demasiado permisiva, un fichero con nombre estable que no se invalida— no lo vas a ver nunca mientras desarrolles sin caché. Lo verá el usuario.

El comportamiento temporal cambia. Sin caché, todos los recursos tardan lo que tarda la red, lo que altera el orden en que llegan las cosas. Una carrera que en producción gana la caché puede perderla en tu máquina, y viceversa.

La costumbre razonable es tenerla activada mientras desarrollas —porque ahí el objetivo es ver tus cambios— y desactivarla explícitamente cuando el objetivo sea medir o reproducir un bug de usuario.

Verificar el estado de la caché desde la consola

Para saber qué se está sirviendo desde dónde, sin depender de la vista.

// Que recursos vinieron de la cache y cuanto ahorraron
(() => {
  const r = performance.getEntriesByType('resource');
  const deCache = r.filter(e => e.transferSize === 0 && e.decodedBodySize > 0);
  const deRed = r.filter(e => e.transferSize > 0);
  console.table([{
    total: r.length,
    desdeCache: deCache.length,
    desdeRed: deRed.length,
    kbAhorrados: Math.round(deCache.reduce((s, e) => s + e.decodedBodySize, 0) / 1024),
    kbTransferidos: Math.round(deRed.reduce((s, e) => s + e.transferSize, 0) / 1024)
  }]);
  console.table(deCache.map(e => ({ recurso: e.name.split('/').pop().slice(0, 45), kb: Math.round(e.decodedBodySize / 1024) })).slice(0, 20));
})();

Y para comprobar el estado del service worker, que es la causa número uno.

// Hay algun service worker mandando aqui
navigator.serviceWorker?.getRegistrations().then(rs => {
  if (!rs.length) return console.log('no hay service workers registrados');
  console.table(rs.map(r => ({
    ambito: r.scope,
    activo: r.active?.scriptURL ?? '',
    esperando: r.waiting?.scriptURL ?? '',
    instalando: r.installing?.scriptURL ?? ''
  })));
  console.log('para quitarlos: navigator.serviceWorker.getRegistrations().then(rs => rs.forEach(r => r.unregister()))');
});

Un worker en estado de espera es una señal muy concreta: hay una versión nueva descargada que no se ha activado porque la antigua sigue controlando pestañas. Ese es el mecanismo por diseño, y es la razón de que un despliegue no se vea hasta cerrar todas las pestañas del sitio.

Una caché mal configurada es peor que no tener caché, y solo se nota en producción

La conversación sobre caché en desarrollo gira siempre alrededor de cómo desactivarla, y eso deja fuera la parte que de verdad importa, que es que una política de caché mal diseñada produce bugs que solo existen en producción y que son de los más difíciles de diagnosticar que hay. El patrón correcto está muy establecido y consiste en dos categorías separadas. Los recursos con nombre versionado —un fichero cuyo nombre contiene un hash de su contenido— se pueden cachear indefinidamente y de forma inmutable, porque si el contenido cambia, el nombre cambia y por tanto es otro recurso; para estos la política es la máxima duración posible y la marca de inmutable, y el resultado es que un usuario recurrente no vuelve a descargarlos nunca. Los recursos con nombre estable —el documento HTML, un manifiesto, un fichero de configuración— no se pueden cachear así porque su URL no cambia cuando su contenido sí; para estos la política es revalidar siempre, o cachear muy poco tiempo. El fallo característico, y es sorprendentemente común, es aplicar una duración larga a un recurso de nombre estable. La consecuencia es un bug que no se puede arreglar desplegando: los usuarios que ya visitaron el sitio tienen una copia guardada durante un año, y ninguna corrección en el servidor los alcanza hasta que expire. He visto equipos desplegar cinco veces preguntándose por qué los usuarios seguían viendo la versión antigua. Y hay un caso todavía peor, que combina lo anterior con service workers: un worker que cachea de forma agresiva y que no tiene una estrategia de actualización correcta puede dejar a un usuario permanentemente anclado a una versión, porque el propio fichero del worker está cacheado y nunca se descarga la versión que arreglaría el problema. Ese escenario ha obligado a más de un equipo a desplegar un worker vacío durante semanas para desactivar el anterior. La lección práctica es que la política de caché es una decisión de arquitectura, no de configuración, y que la casilla de las DevTools no debería usarse para no tener que pensarla.