Prompt y escenarios aplicables
Una página de pago de comercio electrónico se comporta de forma diferente en las navegaciones del historial. A veces, Atrás restaura el DOM exacto, los valores del formulario, la posición de desplazamiento y el montículo (heap) de JavaScript casi instantáneamente. Esa captura restaurada puede contener un total de carrito desactualizado o una sesión que se cerró en otra pestaña. Otras veces, Atrás crea un nuevo documento y ejecuta la ruta de carga normal. El equipo ha estado utilizando el término «caché del navegador» para describir todos estos resultados y ha considerado desactivar el almacenamiento en caché de forma global.
Explica en qué se diferencia la back/forward cache, o bfcache, de la caché HTTP y de la caché de un enrutador SPA. Luego, diseña un plan de ciclo de vida y depuración que:
- detecte una restauración confirmada de bfcache de forma independiente a una carga de documento ordinaria;
- revalide la autenticación, el carrito y otros estados confidenciales antes de permitir una acción consecuente;
- libere recursos mientras la página está oculta y los reconecte exactamente una vez tras una restauración;
- encuentre bloqueadores de elegibilidad en la página, marcos secundarios y código de terceros;
- mida los recorridos del historial, las restauraciones confirmadas, los fallos (misses) y los motivos de bloqueo por versión del navegador;
- preserve el beneficio de rendimiento cuando sea compatible con la política de seguridad del producto.
Esta pregunta se aplica a entrevistas de frontend senior, rendimiento web, plataforma de navegador y arquitectura frontend. Evalúa si el candidato puede razonar sobre un documento pausado y no solo recitar encabezados de caché.
Qué evalúa el entrevistador
Primero, el candidato debe distinguir tres mecanismos diferentes. La caché HTTP almacena respuestas reutilizables y aplica reglas de solicitud, frescura y revalidación. La bfcache puede preservar una página completa entre documentos en la memoria, incluidos su DOM y el montículo de JavaScript, para luego pausarla y reanudarla en el recorrido del historial de sesiones. Un enrutador del cliente puede preservar los datos de la aplicación o la interfaz de usuario para una navegación suave (soft navigation) sin crear ni restaurar un documento administrado por el navegador. Limpiar una caché no es un diagnóstico para las demás.
Segundo, el candidato debe comprender la evidencia del ciclo de vida. pageshow también se dispara en una carga inicial, por lo que el evento por sí solo no es una prueba. Un evento pageshow cuyo valor de persisted sea true confirma que el documento fue restaurado desde una caché como bfcache. En pagehide, persisted: true significa que el navegador tiene la intención de preservar la página; no garantiza que la página permanezca en caché o que se restaure posteriormente.
Tercero, una respuesta sólida trata la restauración como un límite de corrección. Los temporizadores y los valores en memoria se reanudan desde un momento anterior. La autorización sigue perteneciendo al servidor y el estado confidencial debe revalidarse en una restauración confirmada. Es posible que las conexiones activas, los identificadores de bases de datos, los observadores y los bloqueos deban cerrarse o desconectarse antes de la navegación y abrirse de nuevo tras el regreso. El código de reconexión debe ser idempotente.
Cuarto, el candidato debe depurar con evidencia en lugar de una lista universal de bloqueadores. La elegibilidad cambia según el navegador, la versión, el marco, la API, la política de respuesta, la presión de memoria y el escenario de navegación. Chrome DevTools puede ejecutar una prueba aislada de bfcache y clasificar los bloqueadores. Donde sea compatible, PerformanceNavigationTiming.notRestoredReasons agrega evidencia de campo y un árbol de marcos, pero sus cadenas de motivos pueden cambiar y los detalles de origen cruzado pueden estar enmascarados.
Finalmente, el candidato debe definir un resultado medible. Un recorrido del historial reportado como back_forward no es en sí mismo una prueba de un acierto (hit) de bfcache. Los aciertos confirmados provienen de pageshow.persisted. Los fallos son nuevas cargas de documentos alcanzadas a través del recorrido del historial, y sus causas deben segmentarse en lugar de adivinarse. El objetivo es una mayor cobertura de restauración segura sin acciones de sesión desactualizadas, conexiones duplicadas o analíticas distorsionadas.
Preguntas para aclarar antes de responder
- ¿Qué navegación está involucrada? ¿Es una navegación de documento completo, un cambio de ruta suave de SPA, una recarga, una pestaña reabierta o un recorrido real de Atrás/Adelante en la misma pestaña?
- ¿Qué puede sobrevivir de forma segura? Los borradores de formularios y la posición de desplazamiento pueden ser deseables; la autenticación, el precio, el inventario, los permisos y los tokens de un solo uso requieren una regla de frescura autoritativa.
- ¿Qué requiere la política de respuesta? Algunas páginas contienen datos que la política del producto prohíbe mantener en una captura de página. Ese requisito tiene prioridad sobre la tasa de aciertos.
- ¿Qué recursos permanecen activos? Los WebSockets de inventario, las conexiones IndexedDB, los Web Locks, la captura de medios, los observadores y los scripts de terceros pueden afectar la corrección o la elegibilidad.
- ¿Qué navegadores y versiones importan? La salida de DevTools y
notRestoredReasonsno son un contrato portátil. Prueba la matriz de navegadores compatibles con navegación de historial real. - ¿Hay marcos secundarios presentes? Un secundario del mismo origen puede exponer un árbol de bloqueadores detallado. Un marco de origen cruzado puede mostrar solo información enmascarada, lo que requiere aislamiento del proveedor o una reproducción mínima.
- ¿Qué significa «desactualizado»? Define la antigüedad máxima aceptable y la acción que se bloquea mientras la revalidación falla.
- ¿Cómo se contabilizan las analíticas? Decide si una página restaurada es una vista de página, una navegación o ambas, y luego evita que una sola restauración emita eventos duplicados.
- ¿Cuál es el criterio de éxito? Realiza un seguimiento de la proporción de restauraciones confirmadas entre los recorridos de historial observables, los motivos de fallo, la latencia de restauración, los fallos de revalidación, la prevención de acciones desactualizadas y el recuento de recursos duplicados.
Estructura de respuesta de 30 segundos
«Separo bfcache de las cachés HTTP y de enrutador: pausa un documento para la navegación de historial administrada por el navegador. Confirmo los aciertos únicamente con pageshow.persisted, usando pagehide para una limpieza idempotente sin asumir la restauración. En la restauración, revalido la sesión, el carrito, el precio y los permisos antes de acciones confidenciales, y luego reconecto los recursos una sola vez. Reproduzco los fallos en los navegadores compatibles, uso el panel bfcache de Chrome y los datos disponibles de notRestoredReasons, y segmento las métricas de campo por navegador y ruta. Optimizo las páginas seguras manteniendo como autoritativas la autorización del servidor y las políticas de no captura».
Análisis detallado paso a paso
Paso 1: Modelar las cuatro rutas observables
Comienza con los resultados en lugar de los encabezados:
| Ruta | ¿Nuevo documento? | Evidencia principal | Manejo requerido |
|---|---|---|---|
| Navegación inicial o por enlace | Sí | pageshow.persisted === false; tipo de navegación usualmente navigate | Inicializar estado y recursos |
| Recarga | Sí | pageshow.persisted === false; tipo de navegación reload | Ejecutar ruta de carga normal |
| Recorrido del historial con recarga | Sí | pageshow.persisted === false; tipo de navegación back_forward | Registrar un fallo de bfcache e inspeccionar motivos |
| Restauración confirmada de bfcache | No | pageshow.persisted === true | Revalidar estado confidencial y reanudar con seguridad |
El tipo de navegación describe cómo se llegó a un nuevo documento. No puede confirmar un acierto de bfcache porque un acierto reanuda el documento anterior. Sin embargo, puede identificar una nueva carga causada por el recorrido del historial y, por lo tanto, contribuir al denominador de fallos. El reinicio del navegador, la duplicación de pestañas y los flujos de reapertura pueden desdibujar esta señal, por lo que las métricas de campo deben incluir navegador, versión, ruta y escenario cuando estén disponibles.
La bfcache también es diferente de la revalidación HTTP. En una restauración, el navegador reanuda la página en memoria; Cache-Control: no-cache no fuerza una validación de red antes de que aparezcan los píxeles. Si el contenido restaurado debe estar actualizado, la aplicación realiza una verificación dirigida después de pageshow. Una navegación suave de SPA cambia el historial y la interfaz de usuario dentro del mismo documento; bfcache se vuelve relevante cuando ese documento se abandona y posteriormente se restaura.
Paso 2: Hacer que el trabajo del ciclo de vida sea idempotente y seguro ante restauraciones
Centraliza la propiedad de las conexiones. La limpieza puede ser seguida por un documento nuevo, una restauración o ningún regreso en absoluto. La configuración puede ejecutarse tras la carga inicial y después de múltiples restauraciones, por lo que ambas operaciones deben tolerar la repetición.
let liveSocket = null;
function connectLiveUpdates() {
if (liveSocket) return;
liveSocket = new WebSocket("wss://shop.example/live-cart");
}
function disconnectLiveUpdates() {
liveSocket?.close();
liveSocket = null;
}
async function revalidateSensitiveState() {
const response = await fetch("/api/session-snapshot", {
cache: "no-store",
credentials: "same-origin",
});
if (response.status === 401) {
location.replace("/login");
return false;
}
if (!response.ok) {
showRetryState();
return false;
}
renderSessionState(await response.json());
return true;
}
window.addEventListener("pagehide", disconnectLiveUpdates);
window.addEventListener("pageshow", async (event) => {
reportNavigation(event.persisted ? "bfcache_restore" : "document_load");
if (event.persisted && !(await revalidateSensitiveState())) return;
connectLiveUpdates();
});El endpoint del servidor del ejemplo sigue siendo la autoridad. La interfaz de usuario debe deshabilitar el proceso de pago mientras se verifica el estado confidencial restaurado, y una verificación fallida debe mostrar un estado de reintento en lugar de confiar ciegamente en la captura. Una solicitud de pago en el servidor aún debe reautorizar al usuario y recalcular el precio y el inventario.
No utilices unload para limpiezas críticas. No es confiable y puede hacer que las páginas no sean elegibles en algunos navegadores. Prefiere pagehide para la limpieza del ciclo de vida de la página y usa visibilitychange cuando el requisito sea la visibilidad en lugar de la navegación. Evita que las APIs exclusivas de Chromium freeze y resume sean la única ruta de corrección.
Paso 3: Diagnosticar un fallo desde la reproducción local hasta la asignación de propiedad
Reproduce una ruta con una secuencia determinista: ábrela directamente, establece el estado relevante, sigue un enlace normal hacia un segundo documento y luego usa el botón Atrás del navegador. Evita extensiones y herramientas de desarrollo en la ejecución de control porque pueden alterar el comportamiento del ciclo de vida. Repite con los encabezados de respuesta de producción y los scripts de terceros.
En Chrome, ejecuta la prueba de Back/forward cache en el panel Application. Registra si el resultado es accionable, pendiente de soporte del navegador o no accionable, y expande la atribución de marcos. Si aparece un listener de unload, averigua si lo registró código propio o de un proveedor. Si aparece una política de respuesta, conexión abierta, transacción activa, bloqueo, API de medios o marco, redúcelo a la característica real más pequeña que reproduzca el resultado.
Donde sea compatible, inspecciona notRestoredReasons en un nuevo documento alcanzado a través de un recorrido de historial fallido. Conserva la jerarquía devuelta y los valores de motivo como datos de diagnóstico, no como lógica de la aplicación. No codifiques cadenas de motivos exactas de forma rígida, no asumas que todos los navegadores exponen la propiedad ni interpretes null por sí solo como un acierto confirmado. Los detalles de secundarios de origen cruzado pueden enmascararse por privacidad; prueba el proveedor incrustado por separado o elimínalo en un experimento controlado para establecer la causalidad.
Corrige un responsable a la vez, vuelve a ejecutar la prueba aislada y luego verifica toda la ruta. Cerrar una conexión IndexedDB o WebSocket en pagehide, eliminar un manejador de unload y restringir un marco de terceros son ejemplos de hipótesis; el resultado del navegador decide si el cambio realmente afecta la elegibilidad.
Paso 4: Verificar la corrección y el rendimiento en el campo
Emite un evento por cada pageshow. persisted: true es una restauración confirmada. Para cargas de documentos nuevos, adjunta el tipo de Navigation Timing; back_forward identifica un recorrido de historial observable que no entró en bfcache. Mantén esos eventos mutuamente excluyentes. Agrega familia de rutas, navegador, versión, estado de autenticación y cohorte de experimento sin registrar contenido privado de la página.
Mide al menos:
- restauraciones confirmadas divididas por los recorridos de historial observables, segmentadas por navegador y ruta;
- recuentos de fallos y familias de motivos de bloqueo compatibles, incluidos casos enmascarados y no disponibles;
- latencia de restauración a la siguiente pintura (restore-to-next-paint) y de restauración a estado interactivo confidencial;
- fallos de revalidación de sesión, permisos, carrito, precio e inventario;
- intentos de pago bloqueados mientras el estado restaurado no esté verificado;
- efectos secundarios duplicados de sockets, observadores, analíticas y temporizadores tras ciclos repetidos de Atrás/Adelante;
- regresiones de memoria y fallos visibles para el usuario, porque maximizar la tasa de aciertos no es el único objetivo.
Ejecuta ciclos de historial repetidos, cierra sesión en otra pestaña, cambia el carrito en otro lugar, vence un token de un solo uso, haz fallar el endpoint de revalidación y prueba marcos de origen cruzado. Prueba las versiones de Chrome, Firefox y Safari dentro del alcance. Un panel local de Chrome demuestra un caso controlado; la telemetría de producción demuestra la distribución y las aserciones del producto demuestran la seguridad.
Ejemplo de respuesta de alta calidad
«La ruta instantánea es bfcache: el navegador preservó todo el documento de pago y su montículo de JavaScript, lo pausó y lo reanudó durante la navegación por el historial de la sesión. La caché HTTP solo almacena respuestas, mientras que un enrutador de cliente maneja la navegación suave dentro de un documento activo. Primero etiquetaría las rutas. pageshow.persisted === true confirma una restauración. Un nuevo documento cuyo tipo de Navigation Timing es back_forward representa un recorrido de historial que no restauró esta página desde bfcache. pagehide.persisted === true es solo la intención del navegador de almacenar en caché, por lo que no lo usaría como registro de acierto».
«En pagehide, cierro los recursos que no deben permanecer activos, como el socket del carrito y cualquier transacción abierta, utilizando una limpieza idempotente. En cada pageshow, me aseguro de que los recursos se conecten exactamente una vez. Para una restauración confirmada, bloqueo temporalmente el pago, obtengo una captura autoritativa de la sesión y del carrito, actualizo la interfaz de usuario y redirijo en caso de cierre de sesión. La API de pago todavía verifica la autorización, el precio actual y el inventario, por lo que un montículo reanudado nunca puede autorizar una compra por sí mismo. Contabilizo una restauración una sola vez para las analíticas en lugar de volver a ejecutar todos los efectos de la carga inicial».
«Para los fallos, reproduzco la ruta exacta de documento completo con encabezados y proveedores de producción. En Chrome, ejecuto la prueba de bfcache del panel Application, expando la propiedad de los marcos y corrijo las causas accionables una a la vez. En versiones compatibles de Chromium recopilo notRestoredReasons para cargas de historial fallidas, preservando casos enmascarados y desconocidos, y sin condicionar nunca el comportamiento del producto al texto de los motivos. Luego pruebo la matriz compatible de Chrome, Firefox y Safari porque la elegibilidad varía según la implementación y el escenario».
«El criterio de lanzamiento es una mayor proporción de restauraciones confirmadas con menor latencia de retorno, mientras que las acciones con sesiones desactualizadas, conexiones duplicadas y duplicación de analíticas se mantienen en cero en las pruebas. Segmento los fallos y los errores de revalidación por ruta y navegador. Si la política establece que una página que contiene datos privados específicos nunca debe capturarse, mantengo esa restricción y optimizo las páginas circundantes en lugar de sacrificar la seguridad por la tasa de aciertos».
Errores comunes y mejoras
- Llamar «caché del navegador» a cada ruta de retorno → Las respuestas HTTP, las capturas de documentos completos y el estado del enrutador obedecen reglas diferentes → Nombra el mecanismo y recopila evidencia específica del mecanismo.
- Usar solo
pageshowcomo prueba → También se dispara en la carga inicial del documento → Exigeevent.persisted === truepara una restauración confirmada. - Tratar
pagehide.persistedcomo un acierto futuro → El navegador puede desalojar la página más tarde o elegir otra ruta → Úsalo solo para guiar una limpieza segura; registra los aciertos en la restauración. - Asumir que
back_forwardsignifica bfcache → Puede describir un nuevo documento cargado por el recorrido del historial → Combina el tipo de navegación para fallos conpageshow.persistedpara aciertos. - Desactivar todo el almacenamiento en caché para corregir la interfaz desactualizada → Esto oculta el error del ciclo de vida y sacrifica la navegación instantánea → Revalida campos confidenciales y mantén la autorización del servidor como autoritativa.
- Recargar cada página restaurada → Una recarga indiscriminada descarta la optimización y puede entrar en bucle ante un error → Actualiza solo el estado autoritativo, reservando la recarga o redirección para un estado no válido definido.
- Memorizar una lista permanente de bloqueadores → La elegibilidad y los nombres de los motivos cambian entre motores y versiones → Utiliza pruebas de navegador, detección de características y evidencia en campo.
- Poner la limpieza en
unload→ El evento no es confiable y puede impedir la elegibilidad → Usapagehide, además de eventos de visibilidad cuando el requisito del producto sea la visibilidad. - Reconectar en cada evento del ciclo de vida →
pageshow,resumey los efectos de frameworks pueden crear sockets u observadores duplicados → Asigna a cada recurso un único propietario idempotente. - Confiar en una sola pasada de DevTools → No cubre proveedores reales, presión de memoria, navegadores o cambios de sesión → Combina pruebas controladas, una matriz de navegadores, RUM y flujos de producto adversos.
Preguntas de seguimiento
¿Cache-Control: no-cache fuerza la validación antes de una restauración de bfcache?
No. Una restauración de bfcache reanuda un documento en memoria en lugar de resolver sus solicitudes de recursos a través de la caché HTTP, por lo que las directivas de revalidación HTTP no se ejecutan antes de que se muestre la página restaurada. Usa pageshow.persisted para activar una verificación de frescura dirigida. Si la política prohíbe retener la captura de la página por completo, aplica la política de respuesta y de navegador adecuada, aceptando que la elegibilidad puede variar según la implementación.
¿Es PerformanceNavigationTiming.type === "back_forward" una señal de acierto?
No. Le indica a un documento recién creado que fue alcanzado a través del recorrido del historial. Una restauración confirmada de bfcache reanuda un documento existente y se identifica con pageshow.persisted === true. Cuenta las nuevas cargas de back_forward como fallos observables, sujetos a particularidades del navegador como reinicios o escenarios de pestañas reabiertas, y segmenta la telemetría en consecuencia.
¿Qué pasa si notRestoredReasons no existe, es null o está enmascarado?
Trata la ausencia como no compatible, null como insuficiente por sí solo y el enmascaramiento como una categoría de diagnóstico que preserva la privacidad. Confirma los aciertos con el evento de transición de página. Para los fallos, reproduce localmente, inspecciona la salida disponible de DevTools, aísla marcos secundarios y scripts de terceros, y mantén un grupo de desconocidos en lugar de inventar una causa.
¿Cómo deben manejar las analíticas una página restaurada?
Primero elige una definición de producto. Si una restauración cuenta como una vista de página, emite un evento desde pageshow.persisted y evita repetir la instrumentación de carga inicial. Incluye un campo de tipo de navegación para que los analistas puedan separar las cargas de documentos de las restauraciones. Restablece los acumuladores de rendimiento del ámbito de la visita cuando corresponda y prueba ciclos repetidos de Atrás/Adelante para evitar duplicaciones.
¿Debería una página de pago autenticada usar bfcache?
Solo si la política de seguridad permite una captura de la página y la ruta de restauración puede hacerse segura. En la restauración, revalida la sesión y los datos consecuentes, bloquea las acciones confidenciales hasta que se complete la verificación y mantén obligatorias la autorización y la verificación de precios en el servidor. Si el contenido privado no debe permanecer recuperable en el historial, aplica esa política y enfoca la optimización de bfcache en rutas menos confidenciales.