Monitoring bfcache blocking reasons
Limited availability
This feature is not Baseline because it does not work in some of the most widely-used browsers.
Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.
The PerformanceNavigationTiming.notRestoredReasons
property reports information on why the current document was blocked from using the bfcache on navigation. Developers can use this information to identify pages that need updates to make them bfcache-compatible, thereby improving site performance.
Back/forward cache (bfcache)
Modern browsers provide an optimization feature for history navigation called the back/forward cache (bfcache). This enables an instant loading experience when users go back to a page they have already visited. Pages can be blocked from entering the bfcache or get evicted while in the bfcache for different reasons, some required by a specification and some specific to browser implementations.
To enable monitoring bfcache blocking reasons, the PerformanceNavigationTiming
class includes a notRestoredReasons
property. This returns a NotRestoredReasons
object containing related information on the top-level frame and all <iframe>
s present in the document:
- Reasons why bfcache usage was blocked.
- Details such as frame
id
andname
, to help identify<iframe>
s in the HTML.
Note:
Historically, the deprecated PerformanceNavigation.type
property was used to monitor the bfcache, with developers testing for a type
of "TYPE_BACK_FORWARD"
to get an indication of the bfcache hit rate. This however did not provide any reasons for bfcache blocking, or any other data. The notRestoredReasons
property should be used to monitor bfcache blocking, going forward.
Logging bfcache blocking reasons
Ongoing bfcache blocking data can be obtained using a PerformanceObserver
, like this:
const observer = new PerformanceObserver((list) => {
let perfEntries = list.getEntries();
perfEntries.forEach((navEntry) => {
console.log(navEntry.notRestoredReasons);
});
});
observer.observe({ type: "navigation", buffered: true });
Alternatively, you can obtain historical bfcache blocking data using a suitable method such as Performance.getEntriesByType()
:
function returnNRR() {
const navEntries = performance.getEntriesByType("navigation");
for (let i = 0; i < navEntries.length; i++) {
console.log(`Navigation entry ${i}`);
let navEntry = navEntries[i];
console.log(navEntry.notRestoredReasons);
}
}
The code snippets shown above will log NotRestoredReasons
objects to the console. These objects have the following structure, which represents the blocked state of the top-level frame:
{
children: [],
id: null,
name: null,
reasons: [
{ reason: "unload-listener" }
],
src: "",
url: "example.com",
}
The properties are as follows:
children
Read only Experimental-
An array of
NotRestoredReasons
objects, one for each child<iframe>
embedded in the current document, which may contain reasons why the top-level frame was blocked relating to the child frames. Each object has the same structure as the parent object — this way, any number of levels of embedded<iframe>
s can be represented inside the object recursively. If the frame has no children, the array will be empty; if the document is in a cross-origin<iframe>
,children
will returnnull
. id
Read only Experimental-
A string representing the
id
attribute value of the<iframe>
the document is contained in (for example<iframe id="foo" src="...">
). If the document is not in an<iframe>
or the<iframe>
has noid
set,id
will returnnull
. name
Read only Experimental-
A string representing the
name
attribute value of the<iframe>
the document is contained in (for example<iframe name="bar" src="...">
). If the document is not in an<iframe>
or the<iframe>
has noname
set,name
will returnnull
. reasons
Read only Experimental-
An array of
NotRestoredReasonDetails
objects, each representing a reason why the navigated page was blocked from using the bfcache. If the document is in a cross-origin<iframe>
,reasons
will returnnull
, but the parent document may show areason
of"masked"
if any<iframe>
s blocked bfcache usage for the top-level frame. See Blocking reasons for more details on the reasons. src
Read only Experimental-
A string representing the path to the source of the
<iframe>
the document is contained in (for example<iframe src="exampleframe.html">
). If the document is not in an<iframe>
,src
will returnnull
. url
Read only Experimental-
A string representing the URL of the navigated page or
<iframe>
. If the document is in a cross-origin<iframe>
,url
will returnnull
.
Reporting bfcache blocking in same-origin <iframe>
s
When a page has same-origin <iframe>
s embedded, the returned notRestoredReasons
value will contain an array of objects inside the children
property representing the blocking reasons related to each embedded frame.
For example:
{
children: [
{
children: [],
id: "iframe-id",
name: "iframe-name",
reasons: [],
src: "./index.html",
url: "https://www.example.com/iframe-examples.html"
},
{
children: [],
id: "iframe-id2",
name: "iframe-name2",
reasons: [
{ "reason": "unload-listener" }
],
src: "./unload-examples.html",
url: "https://www.example.com/unload-examples.html"
},
],
id: null,
name: null,
reasons: [],
src: null,
url:"https://www.example.com"
}
Reporting bfcache blocking in cross-origin <iframe>
s
When a page has cross-origin frames embedded, the amount of information shared about them is limited to avoid leaking cross-origin information. Only information that the outer page already knows is included, and whether the cross-origin subtree caused bfcache blocking or not. No blocking reasons or information about lower levels of the subtree (even if some sub-levels are same-origin) are included.
For example:
{
children: [
{
children: [],
id: "iframe-id",
name: "iframe-name",
reasons: [],
src: "https://www.example2.com/",
url: null
}
],
id: null,
name: null,
reasons: [
{ "reason": "masked" }
],
src: null,
url:"https://www.example.com"
}
For all the cross-origin <iframe>
s, no blocking reasons are reported; for the top-level frame a reason of "masked"
is reported, to indicate that the reasons are being kept hidden for privacy purposes. Note that "masked"
may also be used for hiding user agent-specific reasons; it doesn't always indicate an issue in an <iframe>
.
Blocking reasons
There are many different reasons why blocking could occur. Although the reasons are standardized, developers should avoid depending on specific wording for reasons and be prepared to handle new reasons being added and deleted.
The values listed in the specification are:
"fetch"
-
While unloading, a fetch initiated by the current document (e.g. via
fetch()
) was canceled while ongoing. As a result, the page was not in a stable state that could be stored in the bfcache. "lock"
-
While unloading, held locks and lock requests were terminated, so the page was not in a stable state that could be stored in the bfcache.
"masked"
-
The exact reason is hidden for privacy purposes. This value can mean one of the following:
- The current document has children contained in a cross-origin
<iframe>
, and they prevented storage in the bfcache. - The current Document could not be stored in the bfcache for user agent-specific reasons.
- The current document has children contained in a cross-origin
-
The original navigation that created the current document errored, and storing the resulting error document in the bfcache was prevented.
"parser-aborted"
-
The current document never finished its initial HTML parsing, and storing the unfinished document in the bfcache was prevented.
"websocket"
-
While unloading, an open WebSocket connect was shut down, so the page was not in a stable state that could be stored in the bfcache.
User-agent specific blocking reasons
Additional blocking reasons that may be used by some browsers are also specified:
"audio-capture"
-
The Document requested audio capture permission by using Media Capture and Streams's
getUserMedia()
with audio. "background-work"
-
The Document requested background work by calling
SyncManager
'sregister()
method,PeriodicSyncManager
'sregister()
method, orBackgroundFetchManager
'sfetch()
method. "broadcastchannel-message"
-
While the page was stored in back/forward cache, a
BroadcastChannel
connection on the page received a message and message event was fired. "idbversionchangeevent"
-
The Document had a pending
IDBVersionChangeEvent
while unloading. "idledetector"
-
The Document had an active
IdleDetector
while unloading. "keyboardlock"
-
While unloading, keyboard lock was still active because
Keyboard
'slock()
method was called. "mediastream"
-
A MediaStreamTrack was in the live state upon unloading.
"midi"
-
The Document requested a MIDI permission by calling
navigator.requestMIDIAccess()
. "modals"
-
User prompts were shown while unloading.
-
While unloading, loading was still ongoing, and so the Document was not in a state that could be stored in back/forward cache.
-
The navigation request was canceled by calling
window.stop()
and the page was not in a state to be stored in back/forward cache. "non-trivial-browsing-context-group"
-
The browsing context group of this Document had more than one top-level browsing context.
"otpcredential"
-
The Document created an
OTPCredential
. "outstanding-network-request"
-
While unloading, the Document had outstanding network requests and was not in a state that could be stored in back/forward cache.
"paymentrequest"
-
The Document had an active
PaymentRequest
while unloading. "pictureinpicturewindow"
-
The Document had an active
PictureInPictureWindow
while unloading. "plugins"
-
The Document contained plugins.
"request-method-not-get"
-
The Document was created from an HTTP request whose method was not
GET
. "response-auth-required"
-
The Document was created from an HTTP response that required HTTP authentication.
"response-cache-control-no-store"
-
The Document was created from an HTTP response whose
Cache-Control
header included the "no-store" token. "response-cache-control-no-cache"
-
The Document was created from an HTTP response whose
Cache-Control
header included the "no-cache" token. "response-keep-alive"
-
The Document was created from an HTTP response that contained a
Keep-Alive
header. "response-scheme-not-http-or-https"
-
The Document was created from a response whose URL's scheme was not an HTTP(S) scheme.
"response-status-not-ok"
-
The Document was created from an HTTP response whose status was not an ok status.
"rtc"
-
While unloading, a
RTCPeerConnection
orRTCDataChannel
was shut down, so the page was not in a state that could be stored in the back/forward cache. "sensors"
-
The Document requested sensor access.
"serviceworker-added"
-
The Document's service worker client started to be controlled by a service worker while the page was in back/forward cache.
"serviceworker-claimed"
-
The Document's service worker client's active service worker was claimed while the page was in back/forward cache.
"serviceworker-postmessage"
-
The Document's service worker client's active service worker received a message while the page was in back/forward cache.
"serviceworker-version-activated"
-
The Document's service worker client's active service worker's version was activated while the page was in back/forward cache.
"serviceworker-unregistered"
-
The Document's service worker client's active service worker's service worker registration was unregistered while the page was in back/forward cache.
-
This Document was in the owner set of a
SharedWorkerGlobalScope
. "smartcardconnection"
-
The Document had an active
SmartCardConnection
while unloading. "speechrecognition"
-
The Document had an active
SpeechRecognition
while unloading. "storageaccess"
-
The Document requested storage access permission by using the Storage Access API.
"unload-listener"
-
The Document registered an event listener for the
unload
event. "video-capture"
-
The Document requested video capture permission by using Media Capture and Streams's
getUserMedia()
with video. "webhid"
-
The Document called the WebHID API's
requestDevice()
method. -
The Document used the Web Share API's
navigator.share()
method. "webtransport"
-
While unloading, an open
WebTransport
connection was shut down, so the page was not in a state that could be stored in the back/forward cache. "webxrdevice"
-
The Document created a XRSystem.
Browser compatibility
BCD tables only load in the browser
See also
Note:
This article is adapted from Back/forward cache notRestoredReasons API by Chris Mills and Barry Pollard, originally published on developer.chrome.com
in 2023 under the Creative Commons Attribution 4.0 License.