fix(customer-routes): Cache-Control: no-store on customer endpoints (#470)
The trigger: PR #458 mounted requireCustomerPortalEnabled which 410'd every /api/customer/* + /api/admin/customers/* request when the master toggle was off. Some browsers cached that 410 (no Cache-Control header was set, so heuristic freshness applied — the wrong default for an authenticated/sensitive surface). PR #470 reverted the middleware, but a customer whose tab cached the 410 still saw 410s until they hard-refreshed. Add noStoreCache middleware and mount it in front of both route groups. Every response (200, 4xx, 5xx) now carries `Cache-Control: no-store, no-cache, must-revalidate, private` plus the HTTP/1.0 Pragma + Expires fallbacks. Any future transient error from these endpoints can no longer get pinned in browser or proxy caches and outlive its cause. Cost is one setHeader per request; applied per route group rather than globally so static assets + galleries keep their own caching strategy unchanged. Includes a dedicated unit test pinning the header set so a future cleanup pass can't quietly drop it and re-introduce the bug.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Cache-Control: no-store helper for sensitive endpoints.
|
||||
*
|
||||
* Why a dedicated middleware: shipping the wrong cache-control header
|
||||
* on a session-bearing endpoint is a class-of-bug that bites long
|
||||
* after the original mistake. The PR #458 / PR #470 history is the
|
||||
* concrete trigger:
|
||||
*
|
||||
* - #458 mounted requireCustomerPortalEnabled which 410'd every
|
||||
* /api/customer/* and /api/admin/customers/* request when the
|
||||
* master toggle was off.
|
||||
* - Some browsers cached the 410 (the response carried no explicit
|
||||
* Cache-Control header, so heuristic freshness applied — for an
|
||||
* authenticated/sensitive surface that's the wrong default).
|
||||
* - #470 reverted the middleware, but a customer whose tab cached
|
||||
* the 410 still saw 410s until they hard-refreshed.
|
||||
*
|
||||
* Mounting `noStoreCache` in front of these routes belt-and-braces
|
||||
* the future: any 4xx/5xx (or 200) response from these endpoints
|
||||
* carries `Cache-Control: no-store`, so a transient kill-switch,
|
||||
* permission flip, or backend restart can never get pinned in
|
||||
* intermediate caches.
|
||||
*
|
||||
* No-op cost (one setHeader per request); applied per route group
|
||||
* rather than globally so static assets + galleries keep their
|
||||
* own caching strategy.
|
||||
*/
|
||||
|
||||
function noStoreCache(req, res, next) {
|
||||
// `no-store` is the strongest signal — no cache, no revalidation,
|
||||
// no offline retention. Pair with `private` so any well-behaved
|
||||
// intermediate proxy treats the response as user-specific.
|
||||
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||
res.setHeader('Pragma', 'no-cache'); // HTTP/1.0 fallback for older proxies
|
||||
res.setHeader('Expires', '0');
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = { noStoreCache };
|
||||
Reference in New Issue
Block a user