Volver al blog

Tu script de analítica probablemente está desactivando la caché de avance/retroceso

La caché de avance/retroceso vuelve casi instantánea la navegación con el botón Atrás, pero un solo listener de unload la desactiva para toda la página. Los scripts de rastreo suelen ser los culpables, y CrUX ya mide el daño.

La navegación más rápida de la web es aquella en la que no se carga absolutamente nada. La caché de avance/retroceso (bfcache) ofrece justamente eso, y una sola línea en el script de tu proveedor de analítica puede desactivarla para todas las páginas de tu sitio.

La bfcache es una optimización del navegador que conserva una instantánea completa de la página en memoria —incluido el heap de JavaScript— cuando el usuario navega fuera de ella. Pulsa el botón Atrás y el navegador restaura esa instantánea y reanuda la ejecución, produciendo una carga casi instantánea sin ninguna petición de red. Se estima que las navegaciones de avance y retroceso representan entre el 10 y el 20 % de todas las navegaciones, así que esto no es un caso marginal.

Un solo listener descalifica a toda la página

La elegibilidad para la bfcache es frágil por diseño. El navegador no congelará una página que haya registrado un listener del evento unload, porque unload implica que la página espera ser destruida. En escritorio, Chrome y Firefox vuelven inelegible para la bfcache cualquier página con un listener de unload: sin excepciones, sin créditos parciales.

El listener no tiene por qué ser tuyo. Los scripts de terceros que registran unload desde dentro de tu página, o incluso dentro de un subframe, descalifican al documento de nivel superior. Lighthouse incluye una auditoría dedicada, no-unload-listeners, precisamente porque el código infractor suele ser código que el autor del sitio nunca escribió.

beforeunload ya no descalifica en los navegadores modernos, pero es poco fiable y conviene seguir evitándolo salvo que el usuario tenga cambios sin guardar.

Los scripts de rastreo son los infractores habituales

El evento unload es el lugar clásico para disparar una baliza final —vaciar una sesión, enviar un evento de "página cerrada"—, así que los scripts de rastreo de comportamiento y de publicidad recurren a él constantemente.

El fbevents.js de Facebook registra un manejador de unload y aparece en aproximadamente el 9 % de todas las páginas web según HTTP Archive. La etiqueta de PayPal inyecta un iframe que añade un evento unload, bloqueando la bfcache en muchos flujos de pago. Scripts en subframes como hCaptcha han hecho lo mismo. Ninguno de ellos requiere un cambio en tu propio código para empezar a costarte caro: basta con que un proveedor publique una actualización.

CrUX ya te muestra la factura

Esto solía ser invisible en los datos de campo. Desde el conjunto de datos de marzo de 2024, el Chrome User Experience Report (CrUX) informa de un desglose por navigation_types —incluida la fracción de visitas servidas desde la caché de avance/retroceso—, de modo que puedes ver cuántos usuarios reales se están perdiendo el camino instantáneo.

La correlación es contundente. El análisis de CrUX encontró una relación estadística fuerte (ρ=0.87) entre una alta fracción de back_forward_cache y la instant_lcp_density, es decir, la proporción de cargas con LCP por debajo de 200 ms. Tras la actualización de Google de marzo de 2026, que aumentó el peso de LCP, INP y CLS en el ranking, un bloqueo autoinfligido de la bfcache es una desventaja medible en el percentil 75, no un error de redondeo.

Detectarlo y corregirlo

Abre DevTools, ve a Application → Back/forward cache y haz clic en Run Test. Chrome enumera cada motivo de bloqueo, incluidos los manejadores de unload añadidos por terceros.

Para detectar restauraciones de bfcache en tu propio código, nunca uses unload. Usa pageshow y comprueba persisted:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // restored from bfcache — no fresh page load fired
  }
})

Para impedir que terceros te descalifiquen, define una cabecera de respuesta que prohíba por completo los listeners de unload:

Permissions-Policy: unload=()

Esto neutraliza los manejadores de unload de cualquier script —etiquetas de proveedores, extensiones o tu propio código heredado—, de modo que la página sigue siendo elegible para la bfcache sin importar qué se cargue.

El tracker que nunca la toca

La solución estructural es usar una instrumentación que no tenga ninguna razón para escuchar unload en primer lugar. Un tracker centrado en la privacidad registra una vista de página con una sola petición keepalive y deja que la petición sobreviva a la página por sí sola: sin baliza de cierre, sin manejador de unload, nada que el navegador deba marcar:

fetch('/collect', {
  method: 'POST',
  keepalive: true,
  body: JSON.stringify({ site_id, path: location.pathname })
})

Cuando un tracker necesita reaccionar a que una página pase a segundo plano, la señal correcta es visibilitychange o pagehide, que se disparan sin volver a la página inelegible para la bfcache. El tracker de Monoid pesa aproximadamente 2 KB, no establece cookies y engancha history.pushState para los cambios de ruta en SPA en lugar del ciclo de vida de la página, de modo que no tiene ningún listener de unload que registrar ni nada que desactive tu caché de avance/retroceso.

La analítica de vigilancia grava el rendimiento de formas que no aparecen en una ejecución de laboratorio. Un bloqueo de la bfcache es uno de los más silenciosos: sin error, sin renderizado lento, solo un botón Atrás que vuelve a cargar desde la red cuando debería haber restaurado desde la memoria.

Fuentes

Comments

Loading comments…