Conditions Blocking Chromium's Back-Forward Cache

The Back-Forward Cache (bfcache) is a browser optimization in Chromium that enables instant loading when users navigate backward or forward between pages. Instead of destroying a page when a user navigates away, the browser pauses JavaScript execution and stores a complete snapshot of the page in memory. This article provides a direct breakdown of the specific technical conditions, active APIs, event listeners, and HTTP headers that disqualify a webpage from entering the Chromium bfcache.

The unload Event Listener

The presence of an unload event listener is one of the most common reasons a page fails to enter the bfcache. The unload event was designed to run when a page is permanently destroyed. If Chromium puts a page with an unload listener into the bfcache and later restores it, the page's state might be broken because the teardown code already ran. Conversely, if Chromium skips the unload event to preserve the page, data loss could occur. Therefore, Chromium simply refuses to cache pages using unload listeners.

Developers should use the pagehide or visibilitychange events instead, as both are fully compatible with the bfcache. Note that beforeunload listeners will also block the bfcache if they actively prevent navigation by triggering a confirmation dialog.

Active Network Connections

Because the bfcache pauses a webpage's execution entirely, any active, stateful network connection cannot be maintained while the page is frozen. Chromium will block a page from entering the bfcache if any of the following are open when the user navigates away:

  • WebSockets: An open WebSocket connection with active listeners prevents caching because the frozen page cannot process incoming messages or maintain the connection heartbeat.
  • WebRTC: Pages with an active RTCPeerConnection cannot be cached due to the real-time nature of the media and data streams.
  • Unfinished Network Requests: While standard background fetches can sometimes be aborted safely, certain ongoing fetch() or XMLHttpRequest operations—especially those holding locks or streaming data—will disqualify the page.

To make a page eligible for the bfcache, close all WebSocket and WebRTC connections during the pagehide event and reopen them during the pageshow event.

Database and Storage Locks

Pages that hold exclusive access to storage resources cannot be frozen in memory, as doing so would block other open tabs of the same origin from accessing those resources.

  • IndexedDB: If a page has an open IndexedDB connection that is currently running an active transaction, or if it blocks another tab from upgrading the database version (failing to close on a versionchange event), Chromium will not place it in the bfcache.
  • Web Locks API: If a page is actively holding a lock via navigator.locks.request(), it will be blocked from the cache so it does not indefinitely stall other tabs waiting for the same lock.

HTTP Cache-Control: no-store Header

The Cache-Control: no-store HTTP response header instructs the browser not to save the response in any cache. When applied to the main document response, Chromium treats this as a strict opt-out from the bfcache to protect potentially sensitive user data (such as banking details or personal messages) from remaining in memory after the user leaves the page.

Note that Cache-Control: no-cache or max-age=0 do not block the bfcache; only no-store on the main document resource acts as a blocker.

Active Hardware and Device APIs

Chromium prevents pages from entering the bfcache if they are actively communicating with external hardware, sensors, or system-level media interfaces. These include:

  • WebUSB, WebBluetooth, and WebHID: Active connections to physical devices cannot be paused and resumed safely.
  • Media Streams and Recording: Active use of the camera or microphone via getUserMedia(), or active screen recording via getDisplayMedia().
  • Geolocation and Sensors: Active polling of device orientation, motion sensors, or continuous geolocation tracking.
  • Speech Synthesis and Recognition: Ongoing text-to-speech playback or active voice recognition sessions.

Cross-Window References (window.opener)

If a page opens another page using window.open() without the noopener attribute, the new page retains a reference to the original page via the window.opener property. Because the two pages can synchronously script and manipulate each other's DOM and JavaScript state, Chromium cannot safely freeze the original page in the bfcache without breaking the linked page.

Using rel="noopener" on links or passing noopener in window.open() severs this connection and preserves bfcache eligibility.

Embedded Iframes and Plugins

A top-level page's eligibility for the bfcache is tied to its embedded content.

  • Blocking Iframes: If an embedded <iframe> uses an unload listener, maintains an open WebSocket, or is currently in the middle of a navigation when the user leaves the main page, the entire top-level page may be blocked from entering the bfcache.
  • Plugins: Any page utilizing legacy browser plugins or containing specific embedded <object> or <embed> elements that Chromium cannot freeze will be disqualified.

Insufficient Memory and Cache Limits

Even if a page avoids all technical blockers, Chromium enforces resource constraints. The browser only keeps a limited number of pages in the bfcache at any given time. If a page consumes an excessive amount of RAM, or if the user opens multiple new tabs that exhaust available system memory, Chromium will evict the cached page or refuse to store it entirely.