Server config cached in localStorage not invalidated when server-side configuration changes

Summary

The Bitwarden web vault caches the /api/config response in localStorage
(key: global_config_byServer). When a self-hosted server administrator
changes server-side configuration (e.g., toggles a feature flag or
registration policy), the cached config persists on existing clients
through page refreshes, hard refreshes, and even network cache bypass
in DevTools. The new configuration only takes effect after the user
manually clears site data.

Observed behavior

Reproduced on Safari 17.x (macOS) against a self-hosted Bitwarden-
compatible server (Vaultwarden-derived web vault):

  1. Initial page load with disableUserRegistration: false — “Create
    account” link is visible (correct).
  2. Administrator changes server env var to DISABLE_USER_REGISTRATION=true.
  3. Server endpoint /api/config now returns disableUserRegistration: true
    when accessed directly in the browser.
  4. Web vault page still shows “Create account” link after:
    • Normal page refresh
    • Hard refresh (Cmd+Shift+R / Cmd+Option+R)
    • Web Inspector → Network → “Disable caches” toggle + reload
  5. “Create account” link disappears only after clearing site data via
    Safari Settings → Privacy → Manage Website Data → Remove.

Chrome on the same server showed the updated configuration immediately.

Expected behavior

At least one of the following:

  • The cached config should have a shorter TTL that guarantees eventual
    consistency within a reasonable window (e.g., 5–15 minutes).
  • The client should revalidate the cached config on navigation events
    (login page load, vault unlock, etc.), possibly using HTTP conditional
    requests (If-None-Match / ETag) so a revalidation is cheap.
  • The client should expose a mechanism for a self-hosted administrator
    to invalidate client caches (e.g., by bumping a config version/epoch
    field in the response that clients compare and use to invalidate local
    cache).
  • At minimum, a forced hard-refresh (Cmd+Shift+R) should bypass the
    localStorage cache for config, since users reasonably expect this
    shortcut to refetch from origin.

Why this matters

This behavior is particularly problematic for self-hosted deployments
(Vaultwarden, Bitwarden self-hosted, Warden-Worker, etc.) where admins
routinely change server-side configuration. The current behavior creates
a confusing mismatch between server state and client state that is:

  1. Not diagnosable through standard HTTP caching controls — no
    Cache-Control or ETag header from the server can override this
    because the cache lives in application-level localStorage.
  2. Not cleared by familiar browser operations (“clear cache”, hard
    refresh, disable cache in DevTools).
  3. Discoverable only by inspecting the Storage tab in DevTools, which
    most users (and even many administrators) will not think to check.

For end users, the most visible symptom is stale feature flag state;
for administrators, it’s extended debugging sessions (“I changed the
setting, why isn’t it working?”).

Suggested implementation approaches

  1. TTL reduction: Set a conservative TTL on the config cache (e.g.,
    10 minutes for authenticated state, shorter for the login page where
    registration visibility is decided).
  2. Conditional revalidation: Send If-Modified-Since or If-None-Match
    headers on cached config; server returns 304 if unchanged (cheap)
    or 200 with new body if changed.
  3. Version/epoch field: Include a configVersion or serverEpoch
    field in the config response. Client compares against cached value on
    each session; mismatch forces re-fetch and cache update.
  4. Hard-refresh bypass: Detect Cache-Control: no-cache on the
    navigation request (which is what hard refresh sends) and skip the
    localStorage read path for that load.

Environment

  • Browser: Safari 17.x on macOS 14.x (behavior also observed on other
    versions)
  • Server: Self-hosted Vaultwarden-compatible server
    (qaz741wsd856/warden-worker fork, /api/config returns standard Bitwarden
    config shape)
  • Web vault: Bundled via bw_web_builds v2025.12.0

Happy to provide additional reproduction details or test proposed fixes.

Since you’re using a third-party back-end, I would recommend reaching out in their community spaces, as per their request:

This topic was automatically closed after 4 days. New replies are no longer allowed.