All checks
Securityhighsession-cookie-in-cacheable-responsechecked on every page we scan

A response sets a session cookie and allows shared caching

This page returns older TLS versions in a `Set-Cookie` header while its `Cache-Control` says `public, max-age=…`, which permits a shared cache to store the whole response — the cookie included.

Why it matters

This page returns older TLS versions in a `Set-Cookie` header while its `Cache-Control` says `public, max-age=…`, which permits a shared cache to store the whole response — the cookie included. Any cache between your server and the world may then serve that stored copy to somebody else: a CDN edge node, a corporate proxy, an ISP cache. The next visitor does not merely see the first visitor's page, they receive their session cookie and are logged in as them for every request afterwards. It needs two mistakes at once, which is why it is rare — and it is invisible in development, because there is no shared cache in front of the app on your laptop. It only happens in production, to strangers. The fix is one directive. Send `Cache-Control: private, no-store` on any response that sets a session cookie, and keep `public` for the pages that genuinely are the same for everyone.

How ShipReady detects it

Responses that hand out a session cookie and allow a shared cache to keep it. PAGE-scoped: this is a property of one response, and the route that sets a session cookie is rarely the homepage. Reads headers the crawl already fetched and issues no requests.

Detection is deterministic. ShipReady reports this only when it observes the condition directly, and prefers to miss a real problem over inventing one. Rule version 1.0.0.

How to fix it

This is the prompt ShipReady puts in your report — written to be pasted straight into Cursor, Claude Code, or whichever assistant built the app.

A response that sets a session cookie also permits a shared cache to store it. Fix the caching directive, then check whether anyone was actually served somebody else's session.

SEND THE RIGHT DIRECTIVE on any response that sets a session cookie:

    Cache-Control: private, no-store

`private` tells shared caches (CDNs, proxies) not to store it; `no-store` tells the browser not to write it to disk either. Keep `public` for responses that genuinely are identical for every visitor.

WHERE TO SET IT depends on what issues the cookie:
  • Next.js route handlers and server actions: return the header explicitly, and be aware that a route setting a cookie should not be statically rendered.
  • Express: `res.set('Cache-Control', 'private, no-store')` in the auth middleware, not per-route — one place, so the next login route inherits it.
  • A CDN in front: Vercel, Cloudflare and Fastly all respect `private`, but check you are not overriding it with an edge rule that forces caching by path.

THEN CHECK YOUR EDGE. The origin's header is only half the story — a CDN rule that caches `/*` by path can ignore it. In Cloudflare look for Cache Rules with "Eligible for cache" forced; on Vercel check `vercel.json` headers and any `s-maxage` you set deliberately.

THEN ASSESS EXPOSURE. If a shared cache did store one of these, it served one user's session cookie to whoever asked next. Check your CDN's cache analytics for HITs on the affected path, and if you cannot rule it out, invalidate the cache and rotate session secrets so every existing session is forced to re-authenticate.

WORTH KNOWING: this cannot be reproduced locally. There is no shared cache in front of your laptop, so the bug exists only in production and only for strangers.

Frequently asked questions

What does "A response sets a session cookie and allows shared caching" mean?
This page returns older TLS versions in a `Set-Cookie` header while its `Cache-Control` says `public, max-age=…`, which permits a shared cache to store the whole response — the cookie included.
How serious is it?
ShipReady rates this high. Fix before launch. A real weakness that an attacker can act on.
How do I fix it?
Paste the fix prompt on this page into Cursor, Claude Code or your AI editor. It is the same prompt ShipReady puts in your report.
Can I check my own site?
Yes — ShipReady scans up to ten pages of any public site for free and reports this alongside every other check. The free report lists every issue it finds and shows full evidence and a fix prompt for the critical and high-severity ones; medium and low findings are counted and unlock on Pro.

Related checks

Run this check on your site

ShipReady checks this and 193 other things across up to ten pages of your site, with an AI-ready fix for each. Free, no signup.