Back/forward cache notRestoredReasons API

Find which navigations were blocked from using the bfcache, and why.

The notRestoredReasons property, added to the PerformanceNavigationTiming class, reports information on whether frames present in the document were blocked from using the bfcache on navigation, and why. Developers can use this information to identify pages that need updates to make them bfcache-compatible, thereby improving site performance.

Current status

Step Status
1. Create explainer Complete
2. Create initial draft of specification Not started
3. Gather feedback & iterate on design In progress
4. Origin trial Started
5. Launch Not started

Try out the bfcache notRestoredReasons API

Starting in version 109, the bfcache notRestoredReasons API is available as an origin trial in Chromium. Find updated information on this feature's release schedule by visiting its ChromeStatus.com feature page (see the Chrome roadmap for version release dates).

You can try out the bfcache notRestoredReasons API by registering for the origin trial and using it in your experiments. See Take part in an origin trial for instructions on how to use your token once you are registered.

Concepts and usage

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 bfcache for different reasons, some required by a specification and some specific to browser implementations.

Previously, there was no way for developers to find out why their pages were blocked from using the bfcache in the field, though there was a test in Chrome dev tools. To enable monitoring in the field, the PerformanceNavigationTiming class has been extended to include a notRestoredReasons property. This returns an object containing related information on all frames present in the document:

  • Details such as frame id and name, to help identify them in the HTML.
  • Whether they were blocked from using the bfcache.
  • Reasons why they were blocked from using the bfcache.

    This allows developers to take action to make those pages bfcache-compatible, thereby improving site performance.

Examples

A PerformanceNavigationTiming instance can be obtained from features such as Performance.getEntriesByType() and PerformanceObserver.

For example, you could invoke the following function to return all PerformanceNavigationTiming objects currently present in the performance timeline and log their notRestoredReasons:

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);
  }
}

For history navigations, the PerformanceNavigationTiming.notRestoredReasons property returns an object with the following structure, which represents the blocked state of the top-level frame:

{
  blocked: true,
  children: [],
  id: "",
  name: "",
  reasons: [ "Internal Error", "Unload handler" ],
  src: "",
  url: "a.com"
}

The properties are as follows:

blocked
A boolean value specifying whether the navigated page is blocked from using the bfcache (true) or not (false).
children
An array of objects representing the blocked state of any frames embedded in the top-level frame. Each object has the same structure as the parent object — this way, any number of levels of embedded frames can be represented inside the object recursively. If the frame has no children, the array will be empty.
id
A string representing the id attribute value of the frame (for example <iframe id="foo" src="...">). If the frame has no id, the value will be an empty string.
name
A string representing the name attribute value of the frame (for example <iframe name="bar" src="...">). If the frame has no name, the value will be an empty string.
reasons
An array of strings each representing a reason why the navigated page was blocked from using the bfcache. There are many different reasons why blocking could occur. See the Blocking reasons section below for more details.
src
A string representing the path to the frame's source (for example <iframe src="b.html">). If the frame has no src, the value will be an empty string.
url
A string representing the URL of the navigated page.

For PerformanceNavigationTiming objects that do not represent history navigations, the notRestoredReasons property will return null. This is useful for determining whether bfcache is not relevant to a particular navigation, as opposed to notRestoredReasons not being supported in which case it would return undefined.

Reporting bfcache blocking in same-origin frames

When a page has same-origin frames embedded, the returned notRestoredReasons value will contain an object inside the children property representing the blocked state of each embedded frame.

For example:

{
  blocked: false,
  children: [
    { url: "a.com", src: "b.a.com", id: "b", name: "b", blocked: false, reasons: [], children: [] },
    { url: "a.com", src: "c.a.com", id: "c", name: "c", blocked: true, reasons: [ "BroadcastChannel" ], children: [] },
    { url: "a.com", src: "d.a.com", id: "d", name: "d", blocked: false, reasons: [], children: [] }
  ],
  id: "",
  name: "",
  reasons: [],
  src: "",
  url:"a.com"
}

Reporting bfcache blocking in cross-origin frames

When a page has cross-origin frames embedded, we limit the amount of information shared about them to avoid leaking cross-origin information. We only include information that the outer page already knows, and whether the cross-origin subtree blocked bfcache or not. We don't include any blocking reasons or information about lower levels of the subtree (even if some sub-levels are same-origin).

For example:

{
  blocked: false,
  children: [
    { url: "a.com", src: "c.a.com", id: "c", name: "c", blocked: true, reasons: [ "ScreenReader" ], children: [] },
    /* cross-origin frame */
    { url: "", src: "b.com", id: "d", name: "d", blocked: true, reasons: [], children: [] }
  ],
  id: "",
  name: "",
  reasons: [],
  src: "",
  url:"a.com"
}

If multiple cross-origin frames have blocking reasons, we randomly select one cross-origin iframe and report whether it blocked bfcache or not. For the rest of the frames, we report null for the blocked value. This is to stop bad actors from inferring information about user state on sites they don't control by embedding multiple third-party frames into a page and then comparing the blocking information from each.

{
  blocked: false,
  children: [
    /* cross-origin frames */
    {url: "", src: "b.com", id: "b", name: "b", blocked: null, reasons: [], children: []},
    {url: "", src: "c.com", id: "c", name: "c", blocked: true, reasons: [], children: []},
    {url: "", src: "d.com", id: "d", name: "d", blocked: null, reasons: [], children: []}
  ]
  id: "",
  name: "",
  reasons: [],
  src: "",
  url:"a.com"
}

See the Security and privacy section in the explainer for more details about security and privacy considerations.

Blocking reasons

As we said earlier, there are many different reasons why blocking could occur. We have compiled a handy spreadsheet showing all the reason strings and explaining what they mean.

There are a few major categories of reason that are worth calling out:

  • Circumstantial: This refers to blocking reasons not directly related to the developer's page code. For example a related component crashed, something went wrong with the loading process, the page is in a temporary state that can't be cached, bfcache disabled due to insufficient memory, or a service worker did something to the page that disqualifies it from being cached.
  • Extensions: There are a few different reason messages related to extensions. Generally we combine quite a few different reasons into the "Extensions" reason. We are intentionally vague about extension-related blocking reasons because we don't want to give away too much information about what extensions the user have installed, which ones are active on the page, what they are doing, etc.
  • PageSupportNeeded: The developer's code is using a web platform feature that is otherwise not bfcache blocking, but it is currently in a state that is bfcache blocking. For example, the page currently has a BroadcastChannel with registered listeners, or an open IndexedDB connection. Or the page has registered an unload handler, which currently prevents the bfcache being used in some browsers.
  • SupportPending: The developer's code is using a web platform feature that disqualifies the page from the bfcache, for example the Web Serial API, Web Authentication API, File System Access API, or Media Session API. Or the page is using Cache-Control: no-store, which currently prevents the bfcache being used in some browsers. This category is also used to report the presence of a tool outside the page itself that is blocking the bfcache, such as a screenreader or the Chrome password manager.

Feedback

The Chromium team wants to hear about your experiences with the bfcache notRestoredReasons API.

Tell us about the API design

Is there something about the API that does not work like you expected? Or are there missing methods or properties that you need to implement your idea? Have a question or comment on the security model? File a spec issue on the corresponding [GitHub repo][feedback], or add your thoughts to an existing issue.

Report a problem with the implementation

Did you find a bug with Chromium's implementation? Or is the implementation different from the spec? File a bug at new.crbug.com. Be sure to include as much detail as you can, simple instructions for reproducing, and specify the component as UI > Browser > Navigation > bfcache. Glitch works great for sharing quick and easy repros.

Show support for the API

Are you planning to use the bfcache notRestoredReasons API? Your public support helps the Chromium team prioritize features and shows other browser vendors how critical it is to support them.

Send a tweet to @ChromiumDev using the hashtag #NotRestoredReasons and let us know where and how you are using it.

Helpful links