[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