--- slug: bfcache-gate type: pattern summary: "The launch and debugging rule that keeps pages and web-platform features safe when Chromium freezes a document in the back/forward cache and restores it on history navigation." created: 2026-06-20 updated: 2026-06-20 last_link_verified: 2026-06-20 related: navigation-commit-pipeline: relation: complements note: "The back/forward cache is a history-traversal path through the navigation system; it can restore a prior document rather than run a normal network-and-commit load." rail-performance-model: relation: supports note: "The cache exists to make back and forward navigation feel instant, which is the user-visible performance outcome the RAIL model frames." main-thread-starvation: relation: contrasts-with note: "A cached document's JavaScript state is paused and resumed; the problem is lifecycle safety, not a long task on an active main thread." input-event-pipeline: relation: related note: "Both patterns keep common user actions responsive by avoiding unnecessary main-thread work, but they operate at different lifecycle moments." feature-flag-guarding: relation: supports note: "New web-platform behavior that can affect cached documents needs staged exposure and rollback while BFCache compatibility is verified." intent-ship-pipeline: relation: governed-by note: "BFCache compatibility is part of launch review for new web-platform features because the behavior can affect privacy, lifecycle, and compatibility." --- # Back/Forward Cache Eligibility Gate > **Pattern** > > A named solution to a recurring problem. *Treat BFCache compatibility as a launch gate: a page or platform feature is not ready until it is safe to freeze, keep in memory, restore on history navigation, and diagnose when restoration is denied.* The back/forward cache, usually shortened to **BFCache**, is not the HTTP cache. It is a browser-owned history-navigation cache for whole documents. When the user leaves a page, Chromium may freeze the document, keep its DOM and JavaScript heap in memory, and restore that exact state when the user presses Back or Forward. The fastest navigation is the one the browser doesn't redo. That speed changes the rules. Code that assumes navigation away destroys the document is wrong on a BFCache path. Code that cleans up in `unload`, keeps open resources alive, or forgets that a document can be non-fully-active may make the page ineligible. The gate means designing for that lifecycle first, then using DevTools and `PerformanceNavigationTiming.notRestoredReasons` to explain every miss. ## Context Chromium has several caches, and confusing them causes bad designs. The HTTP cache stores network responses. The code cache stores compiled script artifacts. BFCache stores a live page: the document, its DOM tree, its JavaScript heap, its scroll position, and enough browser-side state to put the user back where they were during a same-tab history traversal. The browser decides whether a document is eligible when the user navigates away. If it is eligible, Chromium freezes or pauses activity that cannot run while the document is inactive. If the user returns through history, the browser restores the document instead of constructing a new one through the normal network and commit path. This makes Back and Forward feel instant, especially on pages whose original load needed substantial script, network, or layout work. The pattern matters to web authors, Chromium feature teams, and downstream embedders for the same reason: a document that is not destroyed must remain safe. It may hold authenticated state, active JavaScript objects, pending timers, resource handles, or feature-specific browser objects. A new platform API that works only when a document is fully active, or that leaks cross-page state when frozen, is not BFCache-safe until it defines what happens during freeze and restore. ## Problem History navigation is common and user-visible, but many page and platform designs still assume the old lifecycle: navigate away, run cleanup, destroy the document, later load a fresh copy. BFCache breaks that assumption deliberately. It keeps the old document alive so the user's next Back or Forward action is cheap. The recurring problem is that eligibility is lost for reasons the team cannot see. An `unload` handler blocks caching. A response policy or open resource handle makes the document unsafe to store. A new web-platform feature fails to specify what happens when its owning document is frozen. Analytics records a "page load" that was really a restore, cleanup code runs at the wrong lifecycle event, or an enterprise embedder cannot explain why one frame restores and another does not. ## Forces - **Instant history navigation vs. document lifecycle assumptions.** Users expect Back and Forward to answer immediately, while old cleanup code expects navigation away to destroy the page. - **Memory retention vs. correctness.** Keeping a whole document alive costs memory and can preserve sensitive state longer than a reload path would. - **Feature ambition vs. frozen-document safety.** New APIs often want active connections, handles, or callbacks; BFCache requires a defined behavior when the owning document is not fully active. - **Compatibility vs. diagnosability.** A page may fail the gate for one frame, one handler, or one resource, and the team needs a reason precise enough to fix. - **Staged rollout vs. ecosystem breakage.** Browser changes to eligibility rules can make more pages fast, but they can also expose lifecycle bugs that sites have carried for years. ## Solution Make BFCache eligibility a hard review gate for page lifecycle code and for new Chromium web-platform features. A design passes only when it can answer four questions: what happens on navigation away, what state may survive, what must pause or close while the document is not fully active, and how a failed restore will be diagnosed. Use `pagehide` and `pageshow` as the lifecycle boundary, not `unload`. The `pagehide` event fires when the page is being hidden or put into the cache, and `pageshow` fires when it is shown again; both expose a `persisted` value that tells code whether BFCache is involved. Cleanup that must run for a true destruction path belongs on the non-persisted branch. State refresh that must run after a BFCache restore belongs on `pageshow` when `persisted` is true. For feature design, require behavior for non-fully-active documents before launch. A feature that owns a browser-side resource must say whether the resource is closed, suspended, detached, or marked ineligible when the document enters BFCache. A feature that can expose privacy-sensitive or origin-sensitive data must say what happens if the page is restored after the user has navigated elsewhere in the same tab. If the feature cannot be made safe, it should block BFCache deliberately and name that blocker in diagnostics. For debugging, start from the browser's reasons rather than guessing. Chrome DevTools includes a Back/Forward Cache diagnostic panel that attempts a navigation and lists restoration blockers. The platform API `PerformanceNavigationTiming.notRestoredReasons` exposes structured reasons for why the current navigation was not restored from BFCache, including frame-level information when available. That object is the operational counterpart to the review rule: every eligibility decision should be explainable in terms a test, dashboard, or agent can inspect. > **⚠️ Do not use unload for cleanup** > > An `unload` handler is the classic BFCache footgun. Prefer `pagehide` for teardown and `pageshow` for restore work, and branch on `event.persisted` when the BFCache path needs different behavior. ## How It Plays Out An enterprise browser fork injects a compliance script into every managed page. The script writes an audit beacon in `unload`, because the original design assumed the page was gone after navigation. Back navigation feels slow across the deployment, and DevTools reports `unload` as the blocker. The fix is to move the beacon to `pagehide`, use `navigator.sendBeacon()` only for the non-persisted path, and leave long-lived page state alone on the persisted path. Back navigation becomes instant again, and the audit path still runs when the page is actually leaving memory. A Chromium feature team designs an API that keeps a browser-process handle associated with a document. The first design says nothing about history traversal. During review, BFCache analysis asks what happens when the document becomes non-fully-active. Keeping the handle live would allow callbacks into a frozen document; closing it silently would break restore; serializing it would preserve too much state. The team chooses an explicit ineligibility reason for the first launch, guards the feature behind a flag, and files follow-up work to make the handle suspendable before default-on exposure. The eligibility gate turns an implicit lifecycle bug into a named launch decision. A downstream WebView2 integration sees inconsistent Back behavior after a Chrome milestone update. Some pages restore instantly, while a checkout flow always reloads. Rather than diffing the whole app, the team records `performance.getEntriesByType("navigation")[0].notRestoredReasons` after the failed traversal and finds a single cross-origin subframe with an open resource blocker. The team cannot change that third-party frame, but it can stop blaming Chromium's navigation stack and can brief product support accurately: the top-level page is eligible; one child frame is not. ## Consequences **Benefits.** Back and Forward navigation become a performance feature the project can reason about rather than a lucky fast path. A page that follows the gate avoids lifecycle code that disables the cache by accident. A Chromium feature that passes the gate has specified its behavior while the document is frozen, which makes privacy review and compatibility review sharper. A downstream vendor has diagnostics that point to a reason instead of a vague "BFCache missed." **Liabilities.** BFCache trades CPU and network work for memory. Keeping documents alive increases memory pressure, and a browser under pressure may evict cached entries even when the page is otherwise eligible. The pattern also forces cleanup code to become more precise: some work belongs on true destruction, some on hide, some on restore, and some not at all. That split is harder than a single `unload` hook. The gate can slow feature launch. A new API that touches browser resources, permissions, connections, storage, or origin state now has one more lifecycle to specify. That cost is intentional. A feature that cannot say what happens to a non-fully-active document is not ready for broad exposure, because BFCache makes that state routine rather than exceptional. The diagnostic surface is only as useful as the reasons are stable. A `notRestoredReasons` value can be redacted or frame-scoped for privacy, and DevTools may show a richer local view than production telemetry exposes. Treat the reason as the starting point for a fix, not as a complete substitute for reproducing the navigation with tracing and frame context. ## Notes for Agent Context When adding page-lifecycle code for Chromium-targeted web content, never generate `unload`-based cleanup. Use `pagehide` and `pageshow`, check `event.persisted`, and keep the BFCache restore path distinct from a normal reload path. When implementing or reviewing a Chromium web-platform feature, specify behavior for non-fully-active documents before launch. Decide whether each browser-side resource is suspended, closed, restored, or makes the document BFCache-ineligible, and expose a diagnostic reason when ineligibility is intentional. When debugging slow Back or Forward navigation, inspect DevTools' Back/Forward Cache panel and `PerformanceNavigationTiming.notRestoredReasons` before changing code. Do not assume a reload happened; a BFCache restore preserves DOM and JavaScript state, and a missed restore needs a named blocker. ## Sources The Chromium project's BFCache overview in `docs/bfcache.md` is the primary project source for the implementation model and the rule that new features cannot defer BFCache analysis until after launch. Philip Walton and Barry Pollard's `web.dev` guide to the back/forward cache gives the web-author lifecycle model, including the distinction between BFCache and the HTTP cache, `pagehide` / `pageshow`, `event.persisted`, and common blockers such as `unload`. Chrome DevTools documentation describes the Application-panel Back/Forward Cache test and its blocker report. Chrome's `notRestoredReasons` documentation describes the structured `PerformanceNavigationTiming.notRestoredReasons` API. The blink-dev Intent to Experiment and Intent to Ship threads for desktop BFCache record the rollout, platform limits, enterprise-policy considerations, unload-handler risk, and compatibility work. Rakina Zata Amni's W3C TAG guide, *Supporting BFCached Documents*, frames the standards-side requirement for APIs to work with non-fully-active documents. ## Technical Drill-Down - [Chromium BFCache overview (pinned `962b29a`)](https://chromium.googlesource.com/chromium/src/+/962b29a464673f86a95ce387155c537c83c133b2/docs/bfcache.md) — project-level implementation notes and launch-review expectations for new Chromium features. - [`web.dev` — *Back/forward cache*](https://web.dev/articles/bfcache) — web-author lifecycle guidance, including `pagehide`, `pageshow`, `event.persisted`, and common eligibility blockers. - [Chrome DevTools — *Test back/forward cache*](https://developer.chrome.com/docs/devtools/application/back-forward-cache) — the operational panel for reproducing a traversal and reading the blocker report. - [Chrome for Developers — *Back/forward cache notRestoredReasons API*](https://developer.chrome.com/docs/web-platform/bfcache-notrestoredreasons) — structured diagnostics exposed through `PerformanceNavigationTiming.notRestoredReasons`. - [blink-dev — *Intent to Experiment: Back-forward cache for desktop*](https://groups.google.com/a/chromium.org/g/blink-dev/c/GJsdwulsGiI/m/7OauT6SjBQAJ) — the desktop experiment record, including compatibility and standards considerations. - [blink-dev — *Intent to Ship: Back-forward cache for desktop*](https://groups.google.com/a/chromium.org/g/blink-dev/c/RwWG-z4i_PU) — the launch record for desktop BFCache rollout and its risk analysis. - [W3C TAG — *Supporting BFCached Documents*](https://w3ctag.github.io/bfcache-guide/) — standards-side guidance for API designers whose features interact with non-fully-active documents. --- - [Next: Speculative Navigation Pipeline](speculative-navigation-pipeline.md) - [Previous: Input Event Pipeline](input-event-pipeline.md)