Articles

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.

Takina Takina · · 4 min read
Dashboard showing web performance metrics

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 unload handler is treated as unsafe to freeze, because that handler is expected to run at the moment of navigation. Using pagehide instead (see below) is the fix.
  • Open connections the browser can’t safely suspend, such as an in-progress fetch with a streaming response, an open WebSocket, or an active IndexedDB transaction.
  • Cache-Control: no-store on 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 open BroadcastChannel.

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:

  • pagehide fires when the page is about to be hidden, whether it’s being frozen into the bfcache or fully unloaded. Check event.persisted — if true, the page is going into the bfcache rather than being destroyed.
  • pageshow fires when the page becomes visible again, including when it’s restored from the bfcache. Same flag: event.persisted tells 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 cachebfcache
What’s storedResponse bytes (HTML, JS, CSS, images)A full, running page instance in memory
WhereDisk or memory, keyed by URLBrowser process memory only
RestoresThe raw response — still needs parsing and re-executionThe exact execution state, instantly
Controlled byCache-Control headersNavigation eligibility rules, plus Cache-Control: no-store to opt out
Survives a tab closeYes, for a set durationNo — 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.

The Lycoris Team 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.

#Web Development #Performance #Frontend
Takina 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.

#Web Development #Performance #Frontend