Why Your Cache Headers Matter for Security

HTTP caching is a default-on feature: any resource can be cached by any cache layer unless you explicitly say otherwise. Failing to set or correctly using the Cache-Control header can have real consequences for the security of your website and the privacy of your users.

For responses that contain personalized data, you should take one of two precautions:

  • Set Cache-Control: private to prevent shared caches from storing the resource.
  • Set an appropriate secondary cache key, using Vary: Cookie when the response varies based on cookie values—which is often the case when cookies carry credentials.

Here is what you need to understand about the risks, how caches actually operate, and what to change on your site.

The Spectre Connection

The Spectre vulnerability lets a page read a process's memory. A malicious page can use this to access cross-origin data. Modern browsers have locked down SharedArrayBuffer and high-resolution timers to pages that are cross-origin isolated, and enforce Cross-Origin Embedder Policy (COEP). Under COEP, cross-origin resources must be either public (requested without cookies) or explicitly enabled for sharing via CORS or the CORP header.

COEP does not eliminate Spectre. It makes cross-origin resources unattractive targets: resources loaded without cookies have no user-specific data, and resources sent with CORP: cross-origin are explicitly meant to be shared.

How Caching Enables the Attack

When the Cache-Control header is not set correctly, an attack can proceed in this sequence:

  1. A credentialed resource is cached.
  2. The attacker loads a cross-origin isolated page.
  3. The attacker requests the same resource again.
  4. Since the browser sets COEP:credentialless, the request is made without cookies—but the cache may return the earlier credentialed response.
  5. Through a Spectre exploit, the attacker reads the personalized data.

Browser HTTP caches don't allow this in practice. But there are other cache layers beyond the browser, where the attack can succeed.

Common Misconceptions About HTTP Caches

Misconception: Only the Browser Caches Resources

Reality: multiple cache layers typically exist between the user and your server, each with a different scope.

  • Browser caches are tied to a single user. They exist to avoid fetching the same resource repeatedly.
  • Local proxies may be installed by the user, by their company, or by an internet provider. These often serve multiple users, meaning they function as public caches.
  • Origin server caches and CDNs are controlled by the server side. They store shared responses to cut load and reduce latency.

Misconception: TLS Blocks Intermediate Caching

TLS does protect against passive eavesdropping but not against TLS interception. Users regularly run local proxies for purposes like metered connection sharing, virus inspection or data-loss prevention. These proxies install their own root certificate on user machines and decrypt traffic. As a consequence, HTTPS resources may end up in a local proxy's cache. You cannot assume that HTTPS means uncached.

What You Should Change on Your Website

Sites that handle personal identifying information, and high-traffic sites generally, should update their caching setup now. The biggest risk shows up when a resource's content depends on cookies. Without any guard, an intermediary cache can satisfy a cookie-less request with a response that was originally generated for a credentialed request.

Take one of these actions:

  • Add Cache-Control: private to prevent shared caches from storing the response.
  • Properly set secondary cache keys with Vary: Cookie when the resource varies by cookie.

The default should be to always define Cache-Control or Vary rather than trusting implicit HTTP semantics.

Broader Context

Additional cache-based attacks exist beyond cross-origin isolation. Jake Archibald documents several in How to win at CORS.

Some browsers mitigate by dividing their HTTP cache between credentialed and non-credentialed requests. Firefox already does this. Chrome and Safari do not, though Chrome may adopt the split later. This type of split is separate from—and complements—splitting the cache by top-level origin.

Even browsers that split their own caches cannot fix the problem in local proxy caches. For that reason, the recommendations above remain the reliable way to keep personalized and credentialed responses out of shared intermediaries.