Users press the back button more often than almost any other control in the browser. They expect the previous screen to appear immediately, exactly where they left it. Modern browsers meet that expectation with the back/forward cache, or bfcache. Instead of destroying a page when you navigate away, the browser freezes it in memory. When you return, it restores a snapshot. The browser skips parsing HTML, re-executing JavaScript, and recalculating layout. The result feels instant because the page never fully died.

What bfcache actually does

A normal page load is expensive. The browser must fetch resources, tokenize HTML, build the DOM, run scripts, resolve styles, perform layout, paint pixels, and composite layers. bfcache sidesteps almost all of that by keeping the page alive in a frozen state in RAM. It is not a disk cache. The rendered page, including the JavaScript heap, scroll position, and form state, sits in memory while the user reads the next page. When the user clicks back, the browser thaws the snapshot and fires a pageshow event. The page resumes without touching the network or redoing layout from scratch. For users on slow devices or spotty connections, the difference between a bfcache restore and a fresh load can be hundreds of milliseconds or more.

What breaks it

A developer recently ran a clean experiment to find out exactly what blocks bfcache. They built six simple pages, each testing one suspected blocker, then navigated away and pressed back. The results were clear.

A baseline page with no unusual headers or scripts restored successfully. A page with a beforeunload listener also restored without issue. Surprisingly, a page served with Cache-Control: no-store entered bfcache too, contradicting older guidance. Even a live blog article, which might seem too dynamic to freeze, restored successfully.

Two pages failed. A page with an unload event listener could not be restored. A page with an open WebSocket connection was also blocked. These two failures point to the traps that catch real production sites every day.

The unload event trap

The unload event has long been the go-to signal for last-second cleanup. Developers use it to flush analytics beacons, kill timers, or wipe temporary state. The problem is that bfcache is built on the idea that the page might come back alive. If the browser sees an unload listener, it assumes the page expects total destruction and refuses to freeze it. It does not matter if the attached function is empty. The sheer presence of the listener is enough to veto caching across every modern browser.

The replacement is pagehide. This event fires both when the page is being frozen for bfcache and when it is truly being discarded. If you need to distinguish between the two, the event.persisted property is true when the page is heading into bfcache. For most teardown tasks, though, pagehide covers both paths. Move every bit of cleanup logic out of unload and into pagehide. Then remove every unload listener entirely, including those tucked away in third-party analytics snippets or legacy plugins.

Active connection traps

An open network or storage connection signals that your page is still doing real work. The browser inventories active resources at the moment of navigation. If it finds an open WebSocket, an active WebRTC peer connection, or a lingering IndexedDB connection, it aborts the freeze and tears the page down normally. The snapshot cannot be trusted while bytes might still be flowing.

You should close these resources inside a pagehide listener. Call your WebSocket’s close method. Shut down WebRTC peer connections. Abort or commit any outstanding IndexedDB transactions. If your app needs those channels when the user returns, reopen them inside pageshow. This close-on-pagehide, restore-on-pageshow pattern keeps the page eligible for instant back navigation without losing functionality.

The no-store surprise

For years, conventional wisdom held that Cache-Control: no-store prevented bfcache. Chrome changed that behavior in 2025. A page served with no-store can now enter bfcache. The browser only evicts the frozen snapshot later if authentication states or cookies shift in a way that invalidates the saved state. If you have been using no-store as