Memoria caché atrás y adelante

La memoria caché atrás/adelante (o bfcache) es una optimización del navegador que permite la navegación instantánea hacia atrás y hacia adelante. Mejora significativamente la experiencia de navegación, especialmente para los usuarios con redes o dispositivos más lentos.

En esta página, se describe cómo optimizar tus páginas para la bfcache en todos los navegadores.

Compatibilidad del navegador

bfcache es compatible con Firefox y Safari durante muchos años en computadoras de escritorio y dispositivos móviles.

A partir de la versión 86, Chrome habilitó la bfcache para la navegación entre sitios en Android de un pequeño porcentaje de usuarios. En las versiones posteriores, se lanzó de forma gradual compatibilidad adicional. A partir de la versión 96, bfcache está habilitado para todos los usuarios de Chrome en computadoras de escritorio y dispositivos móviles.

Conceptos básicos de bfcache

bfcache es una caché de memoria que almacena una instantánea completa de una página a medida que el usuario se aleja. Con toda la página en la memoria, el navegador puede restablecerla rápidamente si el usuario decide volver, en lugar de tener que repetir todas las solicitudes de red necesarias para cargar la página.

En el siguiente video, se muestra la cantidad de bfcache que puede acelerar la navegación:

El uso de bfcache hace que las páginas se carguen mucho más rápido durante la navegación hacia atrás y hacia adelante.

Los datos de uso de Chrome muestran que 1 de cada 10 navegaciones en computadoras de escritorio y 1 de cada 5 en dispositivos móviles están hacia atrás o hacia adelante. Por ello, bfcache tiene el potencial de ahorrar mucho tiempo y uso de datos.

Cómo funciona la "caché"

La "caché" que usa la bfcache es diferente de la caché HTTP, que desempeña su propia función a la hora de acelerar las navegaciones repetidas. bfcache es una instantánea de toda la página en la memoria, incluido el montón de JavaScript, mientras que la caché HTTP solo contiene las respuestas para solicitudes realizadas con anterioridad. Debido a que es muy raro que todas las solicitudes necesarias para cargar una página se puedan completar desde la caché HTTP, las visitas repetidas con restablecimientos de bfcache son siempre más rápidas que incluso las navegaciones mejor optimizadas que no son de bfcache.

Sin embargo, crear una instantánea de una página en la memoria implica cierta complejidad en términos de la mejor manera de preservar el código en curso. Por ejemplo, ¿cómo controlas las llamadas a setTimeout() en las que se alcanza el tiempo de espera mientras la página está en la bfcache?

La respuesta es que los navegadores pausan los cronómetros pendientes o las promesas sin resolver para las páginas en la bfcache, incluidas casi todas las tareas pendientes en las colas de tareas en cola de JavaScript, y reanudan las tareas de procesamiento si la página se restablece desde la bfcache.

En algunos casos, como los tiempos de espera y las promesas, el riesgo es bastante bajo, pero, en otros, puede generar un comportamiento confuso o inesperado. Por ejemplo, si el navegador pausa una tarea que es necesaria para una transacción de IndexedDB, esto puede afectar a otras pestañas abiertas en el mismo origen, ya que varias pestañas pueden acceder a las mismas bases de datos de IndexedDB simultáneamente. Como resultado, por lo general, los navegadores no intentan almacenar en caché páginas en medio de una transacción de IndexedDB o mientras usan APIs que podrían afectar a otras páginas.

Para obtener más detalles sobre cómo el uso de la API afecta la elegibilidad de la bfcache de una página, consulta Optimiza tus páginas para la bfcache.

bfcache y apps de una sola página (SPA)

Debido a que bfcache funciona con navegaciones administradas por el navegador, no funciona para "navegaciones de software" dentro de una app de una sola página (SPA). Sin embargo, bfcache aún puede ser útil cuando se sale de una SPA y se regresa a ella.

APIs para observar la bfcache

Aunque la bfcache es una optimización que los navegadores realizan automáticamente, es importante que los desarrolladores sepan cuándo se produce para que puedan optimizar sus páginas en función de ella y ajustar las métricas o la medición del rendimiento en consecuencia.

Los eventos principales que se usan para observar la bfcache son los eventos de transición de página pageshow y pagehide, que son compatibles con la mayoría de los navegadores.

Los eventos más recientes de Page Lifecycle, freeze y resume, también se despachan cuando las páginas entran o salen de la bfcache y, en otras situaciones, por ejemplo, cuando una pestaña en segundo plano se inmoviliza para minimizar el uso de CPU. Estos eventos solo son compatibles con los navegadores basados en Chromium.

Observa cuándo se restablece una página desde bfcache

El evento pageshow se activa inmediatamente después del evento load cuando la página se carga inicialmente y cada vez que se restablece la página desde bfcache. El evento pageshow tiene una propiedad persisted, que es true si la página se restableció desde bfcache y false de lo contrario. Puedes usar la propiedad persisted para distinguir las cargas de páginas normales de los restablecimientos de bfcache. Por ejemplo:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  } else {
    console.log('This page was loaded normally.');
  }
});

En navegadores compatibles con la API de Page Lifecycle, el evento resume se activa cuando se restablecen las páginas desde bfcache (inmediatamente antes del evento pageshow) y cuando un usuario vuelve a visitar una pestaña en segundo plano inmovilizada. Si quieres actualizar el estado de una página después de que se haya inmovilizado (lo que incluye las páginas de la bfcache), puedes usar el evento resume. Sin embargo, si deseas medir la tasa de aciertos de la bfcache de tu sitio, debes usar el evento pageshow. En algunos casos, es posible que debas usar ambos.

Si deseas obtener más información sobre las prácticas recomendadas para la medición de bfcache, consulta Cómo la bfcache afecta las estadísticas y la medición del rendimiento.

Cómo observar cuando una página ingresa a la bfcache

El evento pagehide se activa cuando se descarga una página o cuando el navegador intenta colocarla en la bfcache.

El evento pagehide también tiene una propiedad persisted. Si es false, puedes estar seguro de que esa página no está a punto de ingresar a la bfcache. Sin embargo, que persisted sea true no garantiza que una página se almacenará en caché. Significa que el navegador intends almacenar la página en caché, pero puede haber otros factores que imposibiliten esta operación.

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

De manera similar, el evento freeze se activa inmediatamente después del evento pagehide si persisted es true, pero eso solo significa que el navegador intends almacenar la página en caché. Es posible que aún debas descartarlo por varias razones que se explican más adelante.

Optimiza tus páginas para la bfcache

No todas las páginas se almacenan en la bfcache. Incluso si una página se almacena allí, no permanecerá allí de forma indefinida. En las siguientes páginas, se describe qué hace que las páginas sean aptas para la bfcache y se recomiendan prácticas recomendadas para maximizar la capacidad del navegador de almacenar en caché tu página y obtener mejores tasas de aciertos de caché.

Usa pagehide en lugar de unload

La forma más importante de optimizar para la bfcache en todos los navegadores es nunca usar objetos de escucha de eventos unload. Escucha pagehide en su lugar, ya que se activa cuando una página ingresa a la bfcache y cada vez que se activa unload.

unload es una función antigua, que se diseñó originalmente para activarse cada vez que un usuario sale de una página. Este ya no es el caso, pero muchas páginas web aún funcionan con la suposición de que los navegadores usan unload de esta manera y de que, después de que se activa unload, la página descargada deja de existir. Esto puede dañar la bfcache si el navegador intenta almacenar en caché una página descargada.

En computadoras de escritorio, Chrome y Firefox hacen que las páginas con objetos de escucha unload no sean aptas para la bfcache, lo que reduce el riesgo, pero también hace que muchas páginas no se almacenen en caché y, por lo tanto, se vuelvan a cargar mucho más lentamente. Safari intenta almacenar en caché algunas páginas con objetos de escucha de eventos unload, pero para reducir posibles fallas, no ejecuta el evento unload cuando un usuario sale de la página, lo que hace que los objetos de escucha de unload no sean confiables.

En dispositivos móviles, Chrome y Safari intentan almacenar páginas en caché con objetos de escucha de eventos unload, ya que la falta de confiabilidad de unload en dispositivos móviles reduce el riesgo de fallas. Para dispositivos móviles, Firefox considera que las páginas que usan unload no son aptas para la bfcache, excepto en iOS, que requiere que todos los navegadores usen el motor de renderización de WebKit, por lo que se comporta como Safari.

Para determinar si algún código JavaScript de tus páginas usa unload, te recomendamos que uses la auditoría no-unload-listeners en Lighthouse.

Para obtener información sobre el plan de Chrome para dar de baja unload, consulta Dar de baja el evento de descarga.

Utiliza la política de permisos para evitar que se utilicen controladores de descarga en una página

Algunas secuencias de comandos y extensiones de terceros pueden agregar controladores de descarga a una página, lo que ralentiza el sitio, ya que no es apto para la bfcache. Para evitar esto en Chrome 115 y versiones posteriores, usa una Política de Permisos.

Permission-Policy: unload()

Solo agregar objetos de escucha de beforeunload de manera condicional

El evento beforeunload no hace que tus páginas no sean aptas para la bfcache. Sin embargo, no es confiable, por lo que te recomendamos que solo la uses cuando sea absolutamente necesario.

Un ejemplo de caso de uso para beforeunload es advertir a un usuario que tiene cambios sin guardar que perderá si abandona la página. En este caso, recomendamos agregar objetos de escucha beforeunload solo cuando un usuario tenga cambios sin guardar y, luego, quitarlos inmediatamente después de que se guarden esos cambios, como en el siguiente código:

function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});

Minimizar el uso de Cache-Control: no-store

Cache-Control: no-store es un encabezado HTTP que los servidores web pueden configurar en las respuestas y que indican al navegador que no almacene la respuesta en ninguna caché HTTP. Se usa para recursos que contienen información sensible del usuario, como páginas detrás de un acceso.

Aunque bfcache no es una caché HTTP, históricamente los navegadores excluyeron páginas de bfcache cuando se configura Cache-Control: no-store en el recurso de página (pero no en ningún subrecurso). Chrome está trabajando para cambiar este comportamiento y, al mismo tiempo, mantener la privacidad del usuario. Sin embargo, de forma predeterminada, las páginas que usan Cache-Control: no-store no son aptas para la bfcache.

Para optimizar la bfcache, usa Cache-Control: no-store solo en páginas que contengan información sensible que no se debe almacenar en caché.

En el caso de las páginas que desean entregar siempre contenido actualizado, pero que no incluyen información sensible, usa Cache-Control: no-cache o Cache-Control: max-age=0. Estas le indican al navegador que vuelva a validar el contenido antes de entregarlo y no afectan la elegibilidad de bfcache de una página, ya que restablecer una página desde bfcache no implica la caché HTTP.

Si tu contenido cambia minuto a minuto, recupera las actualizaciones con el evento pageshow en su lugar para mantener la página actualizada, como se describe en la siguiente sección.

Actualiza los datos inactivos o sensibles después del restablecimiento de la bfcache

Si tu sitio mantiene datos de estado del usuario y, en especial, si estos incluyen información sensible del usuario, estos deben actualizarse o borrarse después de que se restablezca una página desde la bfcache.

Por ejemplo, si un usuario sale de su cuenta en un sitio en una computadora pública y el siguiente usuario hace clic en el botón Atrás, los datos inactivos de la bfcache pueden incluir datos privados que el primer usuario esperaba que se borraran cuando salió de la cuenta.

Para evitar situaciones como esta, siempre actualiza la página después de un evento pageshow si event.persisted es true:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Do any checks and updates to the page
  }
});

Con algunos cambios, es posible que desees forzar una recarga completa y conservar el historial de navegación para las navegaciones hacia adelante. El siguiente código verifica la presencia de una cookie específica del sitio en el evento pageshow y se vuelve a cargar si no se encuentra:

window.addEventListener('pageshow', (event) => {
  if (event.persisted && !document.cookie.match(/my-cookie)) {
    // Force a reload if the user has logged out.
    location.reload();
  }
});

Restablecimiento de anuncios y bfcache

Puede ser tentador evitar el uso de bfcache para que tu página pueda publicar un nuevo conjunto de anuncios en cada navegación hacia atrás o adelante. Sin embargo, esto es malo para el rendimiento del sitio y no aumenta constantemente la participación con el anuncio. Por ejemplo, es posible que un usuario desee volver a una página para hacer clic en un anuncio, pero si la página se vuelve a cargar en lugar de restablecerse desde la bfcache, es posible que se muestre un anuncio diferente. Te recomendamos que realices pruebas A/B para determinar la mejor estrategia para tu página.

Para los sitios que desean actualizar anuncios en el restablecimiento de la bfcache, solo puedes actualizar los anuncios en el evento pageshow cuando event.persisted es true sin afectar el rendimiento de la página, como en este ejemplo de Google Publishing Tag. Si deseas obtener más información sobre las prácticas recomendadas para tu sitio, consulta a tu proveedor de anuncios.

Evita las referencias window.opener

En navegadores más antiguos, si una página se abría mediante window.open() desde un vínculo con target=_blank, sin especificar rel="noopener", la página de apertura tendría una referencia al objeto window de la página abierta.

Además de ser un riesgo de seguridad, una página con una referencia window.opener no nula no se puede colocar de forma segura en la bfcache, ya que podría dañar cualquier página que intente acceder a ella.

Para evitar estos riesgos, usa rel="noopener" y evita la creación de referencias window.opener. Este es el comportamiento predeterminado en todos los navegadores modernos. Si tu sitio necesita abrir una ventana y controlarla con window.postMessage() o haciendo referencia directamente al objeto de la ventana, ni la ventana abierta ni el abridor son aptos para la bfcache.

Cerrar las conexiones abiertas antes de que el usuario salga

Como se mencionó anteriormente, cuando una página se coloca en la bfcache, detiene todas las tareas programadas de JavaScript y las reanuda cuando se quita la página de la caché.

Si estas tareas programadas de JavaScript solo acceden a las APIs de DOM o a otras APIs aisladas de la página actual, pausar estas tareas mientras la página no sea visible para el usuario no causará problemas.

Sin embargo, si estas tareas están conectadas a APIs a las que también se puede acceder desde otras páginas en el mismo origen (por ejemplo: IndexedDB, Web Locks y WebSockets), pausarlas puede interrumpir esas páginas, ya que evita que se ejecute el código en ellas.

Como resultado, algunos navegadores no intentarán colocar una página en la bfcache si tiene una de las siguientes opciones:

Si tu página usa alguna de estas APIs, te recomendamos cerrar las conexiones y quitar o desconectar observadores durante el evento pagehide o freeze. Esto permite que el navegador almacene la página en caché de forma segura sin el riesgo de afectar otras pestañas abiertas. Luego, si la página se restablece desde la bfcache, puedes volver a abrir o volver a conectar a esas APIs durante el evento pageshow o resume.

En el siguiente ejemplo, se muestra cómo cerrar una conexión abierta en el objeto de escucha de eventos pagehide a fin de garantizar que las páginas que usan IndexedDB sean aptas para la bfcache:

let dbPromise;
function openDB() {
  if (!dbPromise) {
    dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open('my-db', 1);
      req.onupgradeneeded = () => req.result.createObjectStore('keyval');
      req.onerror = () => reject(req.error);
      req.onsuccess = () => resolve(req.result);
    });
  }
  return dbPromise;
}

// Close the connection to the database when the user leaves.
window.addEventListener('pagehide', () => {
  if (dbPromise) {
    dbPromise.then(db => db.close());
    dbPromise = null;
  }
});

// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());

Realiza pruebas para asegurarte de que tus páginas se puedan almacenar en caché

Las Herramientas para desarrolladores de Chrome pueden ayudarte a probar tus páginas para asegurarte de que estén optimizadas para la bfcache y a identificar cualquier problema que pueda impedir que sean aptas.

Para probar una página, haz lo siguiente:

  1. Navega a la página en Chrome.
  2. En Herramientas para desarrolladores, ve a Aplicación > Memoria caché atrás-adelante.
  3. Haz clic en el botón Run Test. Herramientas para desarrolladores intenta navegar a la página anterior y viceversa para determinar si se puede restablecer la página desde la bfcache.
Panel de caché atrás/adelante en Herramientas para desarrolladores
El panel Back-forward Cache en Herramientas para desarrolladores.

Si la prueba se realiza correctamente, el panel mostrará el mensaje "Se restableció de la memoria caché atrás/adelante". De lo contrario, el panel indicará el motivo. Para obtener una lista completa de los motivos, consulta la Lista de motivos de no restablecimiento de Chromium.

Si el motivo es algo que puedes abordar como desarrollador, el panel lo marcará como Practicidad.

Las Herramientas para desarrolladores informaron que no se pudo restablecer una página desde la bfcache
Una prueba de bfcache fallida con un resultado práctico.

En esta imagen, el uso de un objeto de escucha de eventos unload hace que la página no sea apta para la bfcache. Para solucionar este problema, cambia de unload a pagehide:

window.addEventListener('pagehide', ...);
Qué no debes hacer
window.addEventListener('unload', ...);

Lighthouse 10.0 también agregó una auditoría de bfcache, que realiza una prueba similar. Para obtener más información, consulta los documentos de la auditoría de bfcache.

Cómo la bfcache afecta las estadísticas y la medición del rendimiento

Si usas una herramienta de estadísticas para hacer un seguimiento de las visitas a tu sitio, es posible que notes una disminución en la cantidad total de vistas de página que se informan, ya que Chrome habilita la bfcache para más usuarios.

De hecho, es probable que ya no informes las vistas de página desde otros navegadores que implementan la bfcache, porque las bibliotecas de estadísticas más populares no realizan un seguimiento de los restablecimientos de bfcache como nuevas vistas de página.

Para incluir restablecimientos de bfcache en el recuento de páginas vistas, configura objetos de escucha para el evento pageshow y verifica la propiedad persisted.

En el siguiente ejemplo, se muestra cómo hacerlo con Google Analytics. Es probable que otras herramientas de análisis usen una lógica similar:

// Send a pageview when the page is first loaded.
gtag('event', 'page_view');

window.addEventListener('pageshow', (event) => {
  // Send another pageview if the page is restored from bfcache.
  if (event.persisted) {
    gtag('event', 'page_view');
  }
});

Mide la tasa de aciertos de la bfcache

Para identificar las páginas que aún no usan bfcache, mide el tipo de navegación para las cargas de páginas de la siguiente manera:

// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].type;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

Calcula la tasa de aciertos de la bfcache con los recuentos de las navegaciones back_forward y back_forward_cache.

Los motivos por los que una navegación hacia atrás o hacia adelante podría no usar bfcache

  • Salir del navegador y reiniciarlo
  • Duplicando una pestaña.
  • Cómo cerrar y restablecer una pestaña

En algunos de estos casos, es posible que el navegador conserve el tipo de navegación original y muestre un tipo de back_forward a pesar de que no sean navegaciones hacia atrás o hacia adelante. Incluso cuando los tipos de navegación se informan correctamente, la bfcache se descarta de forma periódica para ahorrar memoria.

Debido a esto, los propietarios de sitios web no pueden esperar una tasa de aciertos de bfcache del 100% para todas las navegaciones de back_forward. Sin embargo, medir su proporción puede ayudar a identificar páginas que evitan el uso de bfcache.

El equipo de Chrome está trabajando en una API de NotRestoredReasons para ayudar a exponer los motivos por los que las páginas no usan bfcache, de modo que los desarrolladores puedan mejorar sus tasas de aciertos de bfcache.

Medición del rendimiento

La bfcache también puede afectar de forma negativa las métricas de rendimiento recopiladas en el campo, específicamente las métricas que miden los tiempos de carga de la página.

Debido a que las navegaciones con bfcache restablecen una página existente, en lugar de iniciar una carga de página nueva, la cantidad total de cargas de páginas recopiladas disminuye cuando se habilita la bfcache. Sin embargo, es probable que las cargas de página que reemplace la bfcache se encuentren entre las cargas de páginas más rápidas de tu conjunto de datos, ya que las cargas de páginas repetidas, incluidas las navegaciones hacia atrás y hacia adelante, suelen ser más rápidas que las cargas de páginas por primera vez debido al almacenamiento en caché HTTP. Por lo tanto, habilitar la bfcache puede hacer que las estadísticas muestren una carga de página más lenta, a pesar de mejorar el rendimiento del sitio para el usuario.

Hay varias formas de abordar este problema. Uno es anotar todas las métricas de carga de página con su respectivo tipo de navegación: navigate, reload, back_forward o prerender. Esto te permite seguir supervisando el rendimiento dentro de estos tipos de navegación, incluso si la distribución general se inclina hacia un valor negativo. Recomendamos este enfoque para las métricas de carga de la página que no se centran en el usuario, como el tiempo hasta el primer byte (TTFB).

En el caso de las métricas centradas en el usuario, como las Métricas web esenciales, una mejor opción es informar un valor que represente con mayor precisión lo que experimenta el usuario.

Impacto en las Métricas web esenciales

Las Métricas web esenciales miden la experiencia del usuario en una página web en varias dimensiones (velocidad de carga, interactividad y estabilidad visual). Es importante que las métricas web esenciales reflejen el hecho de que los usuarios experimentan restablecimientos de bfcache como navegaciones más rápidas que cargas de página predeterminadas.

Las herramientas que recopilan y generan informes sobre las métricas web esenciales, como el Informe sobre la experiencia del usuario en Chrome, tratan los restablecimientos de la bfcache como visitas a la página independientes en su conjunto de datos. Si bien no hay APIs de rendimiento web dedicadas para medir estas métricas después de los restablecimientos de bfcache, puedes aproximar sus valores mediante las APIs web existentes:

Para obtener más detalles sobre cómo bfcache afecta cada métrica, consulta las páginas individuales de las guías de métricas de Métricas web esenciales. Si quieres ver un ejemplo específico de cómo implementar versiones de bfcache de estas métricas, consulta PR para agregarlas a la biblioteca de JS de web-vitals.

Recursos adicionales