How Chromium's Back-Forward Cache Freezes Web Pages
This article explores the mechanics behind Chromium's Back-Forward Cache (bfcache), a browser optimization that enables instant page loading when users navigate backward or forward through their history. It covers how Chromium preserves the complete state of a web page in memory, how the browser pauses active tasks and JavaScript execution, the lifecycle events that manage this frozen state, and the specific conditions that disqualify or evict a page from the cache.
Memory Snapshots vs. Network Caching
To understand the bfcache, it is necessary to distinguish it from the standard HTTP disk cache. The HTTP cache stores individual network responses, such as stylesheets, scripts, and images, so they do not need to be re-downloaded. Even with an HTTP cache, navigating back to a previous page requires the browser to reconstruct the page from scratch by parsing HTML, executing JavaScript, and calculating layouts.
The bfcache bypasses this reconstruction entirely. Instead of storing files, it keeps the fully rendered page alive in the device's RAM. When a user navigates away from a site, Chromium detaches the current view but preserves the Document Object Model (DOM), the layout state, the scroll position, and the entire JavaScript heap in memory.
Freezing the Execution Environment
Keeping a previous page alive in memory presents a major challenge: background pages could continue to consume CPU power, drain battery, or execute code that interferes with the user's current activity. To prevent this, Chromium freezes the page's execution environment the moment it enters the bfcache.
Chromium achieves this by suspending the task queues associated with the page's Blink renderer process. When frozen:
- JavaScript execution halts: Active scripts finish their current synchronous execution, and no further tasks are pulled from the queue.
- Timers are paused: Functions like
setTimeout,setInterval, andrequestAnimationFrameare suspended, their internal clocks frozen until the page is restored. - Promises wait: Any unresolved JavaScript Promises are held in a pending state.
- Media stops: Playing video or audio elements are automatically paused by the browser.
By pausing the task queues at the engine level, Chromium guarantees that the page remains completely dormant while consuming zero CPU cycles, yet remains ready to resume at the exact instruction where it left off.
Navigating the Page Lifecycle
Chromium uses specific DOM lifecycle events to transition pages into and out of the bfcache cleanly.
When a user clicks a link to navigate to a new page, Chromium
evaluates whether the current page is eligible for caching. If it is,
the browser fires the pagehide event on the outgoing page.
This event includes a boolean property called persisted,
which Chromium sets to true. This signals to the web
application that it is not being destroyed, but rather placed into the
bfcache. Immediately after this event completes, the page is frozen.
When the user clicks the Back or Forward button to return to that
page, Chromium reconnects the preserved DOM to the browser viewport,
resumes the suspended task queues, and fires the pageshow
event—again with the persisted property set to
true. Because the DOM and JavaScript state never left RAM,
the page appears on screen instantaneously.
Disqualification and Cache Eviction
Not every page can be safely paused and resumed. Chromium enforces strict rules to prevent broken states, data corruption, and security issues. A page will be disqualified from entering the bfcache under several conditions:
- The
unloadevent: If a page registers a legacyunloadevent listener, Chromium generally excludes it from the bfcache. Theunloadevent was designed under the assumption that the page will be permanently destroyed, so executing it and then restoring the page often breaks web applications. - Active connections and locks: Pages holding exclusive resources that might block other tabs are disqualified. This includes open IndexedDB transactions, WebLocks, active WebRTC streams, or open WebSocket connections.
- Cache-Control headers: Pages served with the
Cache-Control: no-storeHTTP header are often excluded to protect sensitive user data, such as banking information, from remaining in memory after navigation.
Even if a page successfully enters the bfcache, it is not kept forever. Chromium strictly manages memory consumption by limiting the number of pages the bfcache can hold at once. A cached page will be silently evicted and destroyed if the operating system runs low on physical RAM, if the page remains in the cache past a set timeout period (typically a few minutes), or if a background service worker attempts to claim or message the frozen client.