Los usuarios presionan el botón de retroceso con más frecuencia que casi cualquier otro control del navegador. Esperan que la pantalla anterior aparezca de inmediato, exactamente donde la dejaron. Los navegadores modernos cumplen esa expectativa con el caché de retroceso/avance, o bfcache. En lugar de destruir una página cuando navegas fuera de ella, el navegador la congela en memoria. Cuando regresas, restaura una instantánea. El navegador se salta el análisis de HTML, la reejecución de JavaScript y el recálculo del diseño. El resultado se siente instantáneo porque la página nunca murió por completo.

Qué hace realmente el bfcache

Una carga de página normal es costosa. El navegador debe buscar recursos, tokenizar HTML, construir el DOM, ejecutar scripts, resolver estilos, realizar el diseño, pintar píxeles y componer capas. El bfcache evita casi todo eso manteniendo la página viva en un estado congelado en la RAM. No es un caché de disco. La página renderizada, incluyendo el heap de JavaScript, la posición de desplazamiento y el estado del formulario, permanece en memoria mientras el usuario lee la siguiente página. Cuando el usuario hace clic en retroceder, el navegador descongela la instantánea y dispara un evento pageshow. La página se reanuda sin tocar la red ni volver a realizar el diseño desde cero. Para los usuarios con dispositivos lentos o conexiones inestables, la diferencia entre una restauración de bfcache y una carga nueva puede ser de cientos de milisegundos o más.

Qué lo rompe

Un desarrollador realizó recientemente un experimento limpio para descubrir exactamente qué bloquea el bfcache. Creó seis páginas sencillas, cada una probando un posible bloqueador, luego navegó fuera y presionó retroceder. Los resultados fueron claros.

Una página base sin encabezados ni scripts inusuales se restauró con éxito. Una página con un escuchador beforeunload también se restauró sin problemas. Sorprendentemente, una página servida con Cache-Control: no-store también entró en el bfcache, contradiciendo las guías anteriores. Incluso un artículo de un blog en vivo, que podría parecer demasiado dinámico para congelarse, se restauró con éxito.

Dos páginas fallaron. Una página con un escuchador del evento unload no pudo restaurarse. Una página con una conexión WebSocket abierta también fue bloqueada. Estos dos fallos señalan las trampas que atrapan a los sitios de producción reales todos los días.

La trampa del evento unload

El evento unload ha sido durante mucho tiempo la señal predilecta para la limpieza de último segundo. Los desarrolladores lo utilizan para enviar señales de analíticas, detener temporizadores o borrar el estado temporal. El problema es que el bfcache se basa en la idea de que la página podría volver a la vida. Si el navegador detecta un escuchador de unload, asume que la página espera una destrucción total y se niega a congelarla. No importa si la función adjunta está vacía. La mera presencia del escuchador es suficiente para vetar el almacenamiento en caché en todos los navegadores modernos.

El reemplazo es pagehide. Este evento se dispara tanto cuando la página se está congelando para el bfcache como cuando realmente se está descartando. Si necesitas distinguir entre ambos, la propiedad event.persisted es true cuando la página se dirige al bfcache. Sin embargo, para la mayoría de las tareas de limpieza, pagehide cubre ambos caminos. Mueve toda la lógica de limpieza de unload a pagehide. Luego, elimina por completo cualquier escuchador de unload, incluidos aquellos ocultos en fragmentos de analíticas de terceros o complementos heredados.

Trampas de conexiones activas

Una conexión de red o de almacenamiento abierta indica que tu página todavía está realizando un trabajo real. El navegador hace un inventario de los recursos activos en el momento de la navegación. Si encuentra un WebSocket abierto, una conexión de par WebRTC activa o una conexión de IndexedDB persistente, aborta la congelación y destruye la página normalmente. No se puede confiar en la instantánea mientras todavía puedan estar fluyendo bytes.

Deberías cerrar estos recursos dentro de un escuchador pagehide. Llama al método close de tu WebSocket. Cierra las conexiones de par WebRTC. Aborta o confirma cualquier transacción de IndexedDB pendiente. Si tu aplicación necesita esos canales cuando el usuario regresa, vuelve a abrirlos dentro de pageshow. Este patrón de "cerrar en pagehide, restaurar en pageshow" mantiene la página apta para una navegación de retroceso instantánea sin perder funcionalidad.

La sorpresa de no-store

Durante años, la sabiduría convencional sostenía que Cache-Control: no-store impedía el bfcache. Chrome cambió ese comportamiento en 2025. Una página servida con no-store ahora puede entrar en el bfcache. El navegador solo elimina la instantánea congelada más tarde si los estados de autenticación o las cookies cambian de una manera que invalide el estado guardado. Si has estado usando no-store como