[wp-trac] [WordPress Trac] #63636: Enable instant page navigations from browser history via bfcache when sending "nocache" headers

WordPress Trac noreply at wordpress.org
Mon Jul 14 22:45:44 UTC 2025


#63636: Enable instant page navigations from browser history via bfcache when
sending "nocache" headers
-------------------------------------------------+-------------------------
 Reporter:  westonruter                          |       Owner:
                                                 |  westonruter
     Type:  enhancement                          |      Status:  accepted
 Priority:  normal                               |   Milestone:  6.9
Component:  Administration                       |     Version:  6.3
 Severity:  normal                               |  Resolution:
 Keywords:  has-patch dev-feedback needs-unit-   |     Focuses:
  tests                                          |  performance, privacy
-------------------------------------------------+-------------------------
Description changed by westonruter:

Old description:

> In #21938 and #61942 the `no-store` directive was added to responses when
> `nochache_headers()` is called. This happens with every authenticated
> response, and it may often be called when serving unauthenticated
> responses in e-commerce plugins, such as on the cart and checkout pages.
> This `no-store` directive prevents proxies from caching the response so
> that it is not erroneously served to other users. This is a good, however
> `no-store` has a downside: [https://web.dev/articles/bfcache#minimize-no-
> store it disables back/forward cache (bfcache)]. This means that
> authenticated users can get a degraded experience since may not
> experience instant back/forward navigations enabled by the browser's
> bfcache. Furthermore, the lack of bfcache can result in data loss when
> data has been entered via a JavaScript-built UI since this state is lost
> when a page is not restored via bfcache. (See
> [https://github.com/woocommerce/woocommerce/pull/58445#issuecomment-3014404754
> demo video] in WooCommerce.)
>
> (There are other [https://web.dev/articles/bfcache#optimize reasons] for
> why bfcache may be disabled for a page. One example is the use of the
> [https://web.dev/articles/bfcache#never-use-the-unload-event unload
> event]; this was removed in #55491 in order to enable bfcache, only to
> realize that `no-store` had been recently added in #21938 so no
> navigation benefit was gained.)
>
> The use of the `no-store` directive to prevent proxies from caching
> responses is actually redundant in this way with the `private`
> [https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers
> /Cache-Control#private directive]:
>
> > The `private` response directive indicates that the response can be
> stored only in a private cache (e.g., local caches in browsers).
> >
> > You should add the `private` directive for user-personalized content,
> especially for responses received after login and for sessions managed
> via cookies.
> >
> > If you forget to add `private` to a response with personalized content,
> then that response can be stored in a shared cache and end up being
> reused for multiple users, which can cause personal information to leak.
>
> The `no-store` [https://developer.mozilla.org/en-
> US/docs/Web/HTTP/Reference/Headers/Cache-Control#no-store directive] also
> does this, but it prevents the browser from also caching the response in
> bfcache:
>
> > The `no-store` response directive indicates that any caches of any kind
> (private or shared) should not store this response.
>
> Nevertheless, the disabling of the bfcache via `no-store` was actually
> intentional in #21938. With bfcache there is a privacy concern where an
> authenticated user may log out of WordPress, only for another person to
> access the computer and click the back button in order to view the
> contents of the authenticated page loaded from the bfcache. In practice
> this issue depends on the user being on a shared computer, and it also
> requires the malicious user to act soon since the bfcache has a timeout
> ([https://developer.chrome.com/docs/web-platform/bfcache-
> ccns#:~:text=The%20bfcache%20timeout%20for%20Cache%2DControl%3A%20no%2Dstore%20pages%20is%20also%20reduced%20to%203%20minutes%20(from%2010%20minutes%20used%20for%20pages%20which%20don%27t%20use%20Cache%2DControl%3A%20no%2Dstore)%20to%20further%20reduce%20risk.
> 10 minutes] in Chrome for pages sent without `no-store`). As noted in a
> [https://core.trac.wordpress.org/ticket/21938#comment:47 comment], it's
> not entirely clear if the increased privacy is worth the degraded user
> experience if the `private` directive is already preventing proxies from
> caching the responses.
>
> Nevertheless, the privacy concern can still be addressed without
> disabling bfcache. Protecting against restoring pages from bfcache after
> the user has logged out can be achieved as follows: When authenticating
> to WordPress, a "bfcache session token" cookie is sent along with the
> other authentication cookies. This cookie is not HTTP-only so that it can
> be read in JavaScript; it is a random string not used for any other
> purpose. When an authenticated page is served, a script is included which
> reads the value of this cookie. When a user navigates away from the page
> and then navigates back to it, a `pageshow` event handler checks to see
> if it was restored from bfcache. If so, it checks the latest value of the
> cookie, and if it doesn't match it clears the contents of the page and
> initiates a page reload so that the contents are not available.
>
> Related tickets:
>
> * #21938
> * #55491
> * #57627
> * #61942

New description:

 In #21938 and #61942 the `no-store` directive was added to responses when
 `nochache_headers()` is called. This happens with every authenticated
 response, and it may often be called when serving unauthenticated
 responses in e-commerce plugins, such as on the cart and checkout pages.
 This `no-store` directive prevents proxies from caching the response so
 that it is not erroneously served to other users. This is a good, however
 `no-store` has a downside: [https://web.dev/articles/bfcache#minimize-no-
 store it disables back/forward cache (bfcache)]. This means that
 authenticated users can get a degraded experience since may not experience
 instant back/forward navigations enabled by the browser's bfcache.
 Furthermore, the lack of bfcache can result in data loss when data has
 been entered via a JavaScript-built UI since this state is lost when a
 page is not restored via bfcache. (See
 [https://github.com/woocommerce/woocommerce/pull/58445#issuecomment-3014404754
 demo video] in WooCommerce for a `no-store` removal PR which is now
 merged.)

 (There are other [https://web.dev/articles/bfcache#optimize reasons] for
 why bfcache may be disabled for a page. One example is the use of the
 [https://web.dev/articles/bfcache#never-use-the-unload-event unload
 event]; this was removed in #55491 in order to enable bfcache, only to
 realize that `no-store` had been recently added in #21938 so no navigation
 benefit was gained.)

 The use of the `no-store` directive to prevent proxies from caching
 responses is actually redundant in this way with the `private`
 [https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers
 /Cache-Control#private directive]:

 > The `private` response directive indicates that the response can be
 stored only in a private cache (e.g., local caches in browsers).
 >
 > You should add the `private` directive for user-personalized content,
 especially for responses received after login and for sessions managed via
 cookies.
 >
 > If you forget to add `private` to a response with personalized content,
 then that response can be stored in a shared cache and end up being reused
 for multiple users, which can cause personal information to leak.

 The `no-store` [https://developer.mozilla.org/en-
 US/docs/Web/HTTP/Reference/Headers/Cache-Control#no-store directive] also
 does this, but it prevents the browser from also caching the response in
 bfcache:

 > The `no-store` response directive indicates that any caches of any kind
 (private or shared) should not store this response.

 Nevertheless, the disabling of the bfcache via `no-store` was actually
 intentional in #21938. With bfcache there is a privacy concern where an
 authenticated user may log out of WordPress, only for another person to
 access the computer and click the back button in order to view the
 contents of the authenticated page loaded from the bfcache. In practice
 this issue depends on the user being on a shared computer, and it also
 requires the malicious user to act soon since the bfcache has a timeout
 ([https://developer.chrome.com/docs/web-platform/bfcache-
 ccns#:~:text=The%20bfcache%20timeout%20for%20Cache%2DControl%3A%20no%2Dstore%20pages%20is%20also%20reduced%20to%203%20minutes%20(from%2010%20minutes%20used%20for%20pages%20which%20don%27t%20use%20Cache%2DControl%3A%20no%2Dstore)%20to%20further%20reduce%20risk.
 10 minutes] in Chrome for pages sent without `no-store`). As noted in a
 [https://core.trac.wordpress.org/ticket/21938#comment:47 comment], it's
 not entirely clear if the increased privacy is worth the degraded user
 experience if the `private` directive is already preventing proxies from
 caching the responses.

 Nevertheless, the privacy concern can still be addressed without disabling
 bfcache. Protecting against restoring pages from bfcache after the user
 has logged out can be achieved as follows: When authenticating to
 WordPress, a "bfcache session token" cookie is sent along with the other
 authentication cookies. This cookie is not HTTP-only so that it can be
 read in JavaScript; it is a random string not used for any other purpose.
 When an authenticated page is served, a script is included which reads the
 value of this cookie. When a user navigates away from the page and then
 navigates back to it, a `pageshow` event handler checks to see if it was
 restored from bfcache. If so, it checks the latest value of the cookie,
 and if it doesn't match it clears the contents of the page and initiates a
 page reload so that the contents are not available.

 Related tickets:

 * #21938
 * #55491
 * #57627
 * #61942

--

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/63636#comment:6>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list