Skip to content

Session cache limiter blocks bfcache on all public pages #2197

@adrianbj

Description

@adrianbj

Hey @ryancramerdesign - I am sure it's obvious that I got Claude to write this issue, but it does sound like a worthwhile improvement unless I am missing something.

The reason I investigated this is this Lighthouse report:

Image

Description:

PHP's default session.cache_limiter is "nocache", which automatically sends Cache-Control: no-store, no-cache, must-revalidate whenever session_start() is called. Since ProcessWire starts a session on every request, this header is sent on every page — including public, non-authenticated pages. This prevents browsers from using the back/forward cache (bfcache), which significantly impacts perceived performance on back/forward navigations. Lighthouse flags this as "Page prevented back/forward cache restoration."

Current behavior

wire/core/Session.php:330 calls @session_start($options) without first calling session_cache_limiter(''), so PHP's default nocache limiter applies universally.

Suggested behavior

  1. Call session_cache_limiter('') before session_start() to disable PHP's automatic cache headers
  2. Set Cache-Control conditionally based on auth state:
    • Logged-in users: Cache-Control: no-store (current behavior, protects personalized/authenticated pages)
    • Guests: Cache-Control: no-cache (allows bfcache, but still revalidates on normal navigation)

Why no-cache is safe for guests

no-cache does not mean "don't cache." It means "you may store this, but must revalidate with the server before using it." This allows bfcache (in-memory page restoration on back/forward) while still ensuring the browser checks for fresh content on regular navigation. Since guest pages contain no personalized data, there's no risk of exposing sensitive content.

Reproduction

  1. Run Lighthouse on any ProcessWire site
  2. Check the "Back/forward cache" audit
  3. It will report: "Pages with cache-control:no-store header cannot enter back/forward cache"

Confirm with:

php -i | grep session.cache_limiter
# Output: session.cache_limiter => nocache => nocache

Workaround

Site owners can work around this today in site/config.php:

session_cache_limiter('');

and then in site/ready.php:

  // After headers are built, set Cache-Control based on auth state                                                                                                                
  if ($user->isLoggedIn()) {                                                                                                                                                       
      header('Cache-Control: no-store');                                                                                                                                           
  } else {                                                                                                                                                                         
      header('Cache-Control: no-cache');
  }  

But this seems like something PW should handle by default, since the current behavior penalizes every PW site's bfcache on public pages for no security benefit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions