bfcache restore failed medium
Page prevented back/forward cache restoration
The page can't be stored in the back/forward cache, so hitting Back reloads it from the network instead of restoring instantly.
What you see
Page prevented back/forward cache restoration Failure reason: pages with an unload handler are not currently eligible for back/forward cache.
What’s actually happening
You click Back and instead of the previous page snapping into view in a few milliseconds, it spins through a full reload: blank screen, scripts re-run, scroll position lost. Lighthouse flags it under the bfcache audit and Chrome DevTools shows it in the Application > Back/forward cache panel. The hit affects perceived speed on every back/forward navigation, not first load.
Common causes
- An `unload` event listener anywhere on the page (or in a third-party script/iframe) — the single most common blocker.
- `Cache-Control: no-store` on the main document combined with a state change Chrome tracks (a cookie write or HTTPS-only cache entry).
- A still-open connection at navigation time: an in-flight `fetch`, an open WebSocket, WebRTC, or a pending IndexedDB transaction.
- `window.opener` being set (page opened via `target="_blank"` without `rel="noopener"`), or use of the legacy `Cache-Control: no-cache` patterns that keep the page alive.
- An extension or embedded widget injecting `beforeunload`/`unload` handlers you don't control.
How to fix it
- Find the real reason in DevTools, don't guessOpen DevTools > Application > Back/forward cache, click Test back/forward cache (or just navigate away and back). Chrome lists the exact blocking reasons with the offending frame. In production, read `performance.getEntriesByType('navigation')[0].notRestoredReasons` (Chrome 123+) to collect the same data via RUM.
- Replace unload with pagehideSearch the codebase for `addEventListener('unload'` and `onunload`. Swap each for `pagehide`, and move teardown/analytics-beacon logic into `pagehide` or `visibilitychange` with `document.visibilityState === 'hidden'`. `pagehide` fires reliably and does not disqualify bfcache.
- Close open connections on pagehideIn a `pagehide` handler, call `socket.close()` on WebSockets, abort in-flight fetches with an `AbortController`, and let IndexedDB transactions finish. Reopen them in `pageshow` when `event.persisted` is true so a restored page reconnects.
- Stop sending no-store on cacheable HTMLIf the document carries `Cache-Control: no-store` only out of habit, switch to `no-cache` or a short `max-age` with `private`. Reserve `no-store` for genuinely sensitive pages (banking, account settings) where bfcache is undesirable anyway.
- Neutralize third-party blockersIf an analytics or chat widget registers `unload`, update it to a version that uses `pagehide`, or load it in a way that doesn't taint the top frame. Confirm the fix by re-running the DevTools test.
Stop it recurring
Lint for `unload`/`beforeunload` in CI and add a Lighthouse bfcache assertion so a regression fails the build instead of shipping silently.
Related errors