Safety
Cache-Control public vs private for Personalized Pages
May 17, 2022
Cache-Control public vs private is a shared-cache decision, not a performance slogan. public means a CDN or browser may store the response for reuse across users. private means only the end user's browser should keep it. Authenticated or personalized HTML that also sets cookies should be private or no-store — not public. Static assets can stay public with long max-age. RoastMyOpsec inspects Cache-Control versus Set-Cookie on the primary response. It does not poison caches or fetch other users' pages.
The practical difference
A shared cache that stores a page with someone else's session cookie header — or a body that includes their name — will serve that mix to the next visitor. That is the failure mode, not 'the CDN is too fast.'
private still allows the browser to cache. no-store is for responses you do not want replayed from disk at all, such as account dashboards after logout is a concern.
How to choose based on the response
Separate route classes. Marketing HTML can often be public if it is the same for everyone and does not Set-Cookie a session. App HTML that varies by account should be private or no-store. JS/CSS/images with content hashes can be public and immutable.
| Response | Cache-Control | Cookies | Takeaway |
|---|---|---|---|
| Public marketing HTML | public, short max-age or s-maxage | None, or non-session prefs only | Fine when the body is identical for all |
| Logged-in HTML | private, no-store, or no-cache | Session cookies likely | Never public on a shared cache |
| Hashed static assets | public, max-age long, immutable | None | This is where CDNs earn rent |
| API JSON with PII | private or no-store | If used, still not public | Do not let intermediaries reuse bodies |
When public still fails the roast
public on a document that also sends Set-Cookie is the classic miss. Even if the body looks generic, caches can store the cookie semantics poorly and browsers can get confused about what is shareable.
Fix at the origin and at the CDN: explicit rules per path prefix, not one global 'cache everything' toggle.
Common mistakes
The first mistake is copying a static-site Cache-Control onto an app shell.
The second mistake is Vary: Cookie without understanding you just exploded cache keys — or omitted Vary and shared the wrong body.
The third mistake is purging the CDN once and leaving the origin header wrong.
Free audit the URL you own
RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.
Free audit nowFAQ
- Is Cache-Control public a vulnerability?
- Not by itself. It is a miss when the response is personalized or paired with session cookies. Public is appropriate for identical, unauthenticated assets.
- Should I use private or no-store for dashboards?
- private allows the user's browser to reuse the page. no-store is stricter when you do not want that file on disk. Many account pages use private, no-cache plus a solid logout.
- How does RoastMyOpsec grade caching?
- Header inspection on the primary response: Cache-Control versus Set-Cookie combinations. No cache-poisoning tests.