What Is the bfcache? Instant Back/Forward Navigation
The bfcache stores a full, running snapshot of a page in memory so back and forward navigation is instant instead of a fresh load. How it works.
The back/forward cache, or bfcache, is a browser optimization that stores a complete, in-memory snapshot of a page — including its JavaScript execution state, scroll position, and the DOM as it existed — so that navigating back or forward restores it instantly instead of reloading it from scratch. When a page is served from the bfcache, no network request happens at all; the tab just resumes exactly where it left off, usually within a few milliseconds.
Why a normal back button is slow
Without the bfcache, clicking “back” tears the current page down, throws away its JavaScript state, and re-fetches and re-parses the previous page as if you’d never visited it. That means re-running module initialization, re-establishing any state a framework built up, and waiting on the network again — even though the browser rendered that exact page moments ago. For a user rapidly bouncing between a list page and a detail page, that’s a jarring, avoidable delay.
The bfcache fixes this by freezing the page in place rather than destroying it. The tab’s process stays alive, paused, holding the full page state in memory. Navigating back doesn’t reconstruct the page — it just unpauses it.
What makes a page eligible
Browsers only cache a page in the bfcache if they’re confident it’s safe to freeze and later resume without side effects. Several things disqualify a page:
- Unload event listeners. A page that registers an
unloadhandler is treated as unsafe to freeze, because that handler is expected to run at the moment of navigation. Usingpagehideinstead (see below) is the fix. - Open connections the browser can’t safely suspend, such as an in-progress
fetchwith a streaming response, an open WebSocket, or an active IndexedDB transaction. Cache-Control: no-storeon the response, which some sites set specifically to opt a page out of caching, including the bfcache.- Certain browser features held open, like an active
getUserMedia()media stream, a WebRTC connection, or a page with an openBroadcastChannel.
None of these are permanent bans — they’re conditions checked at the moment of navigation away from the page. Close the connection before you leave, and the page becomes eligible again.
pagehide and pageshow, not unload
The practical takeaway for anyone building a site is to stop relying on the unload event entirely. It’s unreliable on mobile browsers, it disqualifies the page from the bfcache, and it was never guaranteed to fire in the first place. The bfcache-compatible replacement is a pair of events:
pagehidefires when the page is about to be hidden, whether it’s being frozen into the bfcache or fully unloaded. Checkevent.persisted— iftrue, the page is going into the bfcache rather than being destroyed.pageshowfires when the page becomes visible again, including when it’s restored from the bfcache. Same flag:event.persistedtells you whether this is a fresh load or a cache restoration.
window.addEventListener("pageshow", (event) => {
if (event.persisted) {
// Restored from bfcache — refresh anything that might be stale,
// like a countdown timer or an auth token's expiry check.
}
});
Anything a page does that assumes it’s the only instance running — polling intervals, timers, open sockets — should pause on pagehide and, if needed, resume on pageshow. A page frozen mid-flight and later resumed can otherwise wake up with stale state or duplicate timers.
Why it matters for Core Web Vitals
A bfcache restoration essentially eliminates load-related metrics for that navigation — there’s no Largest Contentful Paint to compute because nothing is loading. But it still gets measured as its own navigation type, and a stale bfcache restoration can leave event handlers in a broken state, which shows up as sluggish responsiveness on the very first interaction after the page reappears. Chrome’s DevTools includes a dedicated bfcache test under the Application panel that reports exactly which API or header disqualified a page, which is the fastest way to debug an unexpectedly slow back button.
Single-page apps built with client-side routing don’t benefit from the bfcache in the same way, since there’s no full-document navigation to cache — but any multi-page app, including ones built with server-side rendering frameworks like Astro, gets this optimization for free as long as it avoids the disqualifying patterns above.
bfcache vs HTTP caching
It’s worth distinguishing the bfcache from ordinary HTTP caching, since both involve the word “cache” but solve different problems.
| HTTP cache | bfcache | |
|---|---|---|
| What’s stored | Response bytes (HTML, JS, CSS, images) | A full, running page instance in memory |
| Where | Disk or memory, keyed by URL | Browser process memory only |
| Restores | The raw response — still needs parsing and re-execution | The exact execution state, instantly |
| Controlled by | Cache-Control headers | Navigation eligibility rules, plus Cache-Control: no-store to opt out |
| Survives a tab close | Yes, for a set duration | No — it’s tied to the session’s navigation history |
A page can be servable from the HTTP cache and still miss the bfcache, or vice versa. They’re complementary: the HTTP cache speeds up a fresh load, and the bfcache skips the load entirely.
The takeaway
The bfcache turns back and forward navigation into a resume operation instead of a reload, and it’s on by default in every major browser. The one thing that breaks it is code that assumes it owns the tab for its entire lifetime — an unload listener, a socket left open, a response header that opts out. Swap unload for pagehide/pageshow, close connections you don’t need to keep alive across a navigation, and check DevTools’ bfcache diagnostics if a page isn’t getting the instant back button it should.
Keep reading
The Lycoris Team · · 5 min read What Is Incremental Static Regeneration (ISR)?
ISR lets a static page be rebuilt in the background after deploy, on a timer or on demand, without a full site rebuild. How it works and its tradeoffs.
Takina · · 4 min read Stale-While-Revalidate: The Caching Strategy Explained
Stale-while-revalidate serves a cached response instantly while fetching a fresh one in the background, so no request waits on the network.
Takina · · 3 min read Reflow vs Repaint: How Browsers Redraw Pages
Reflow recalculates layout; repaint just redraws pixels. Why the difference matters for animation performance and how to avoid triggering reflow.