Skip to content
Ketryon
Blog

9 min read

Cookies and CDN caching on Vercel for signed-in pages

Vercel no longer CDN-caches responses with Vary: Cookie, affecting security, response times and load on personalised pages.

By Written with AI assistance from the source

Mist over dark coastal cliffs and a calm sea
Photo: Phill Brown on Unsplash

What Vercel has changed

Vercel has changed how the platform's CDN handles responses containing the HTTP header Vary: Cookie. These responses are no longer stored in the shared CDN cache. The visitor still receives the response, but the next request must go back to the application or its origin rather than reusing a stored copy.

The change is described in Vercel's changelog entry from 30 September 2026. When a response is denied storage for this reason, x-vercel-cache shows the value MISS, while Vercel's Runtime Logs give vary_key_denied:cookie as the reason.

It is important to distinguish between a request that contains cookies and a response that specifies Vary: Cookie. Cookies are often used for authentication, session management, consent, language selection and analytics. Their presence alone does not automatically stop CDN caching as a result of this change. The deciding factor is the Vary header from your app, framework or an intermediary proxy.

Vary tells a cache which parts of the request may affect the response. With Vary: Cookie, the server is effectively saying that the content may be different when the cookie header is different. The cache would therefore need to keep responses for different cookie values separate.

The problem is that the cookie header is often nearly unique to each visitor. Session cookies and analytics cookies then create a large number of variants that can rarely be reused. A CDN cache is most useful when many visitors can share the same response. If nearly every request gets its own variant, much of the point of the cache is lost.

The change does not affect other supported Vary headers. It is specifically aimed at headers with so many possible values that shared caching becomes unsuitable.

Why the change matters for security

A shared CDN cache sits between the visitor and your application. It is designed to reuse the same content for multiple visitors. This works well for public product pages, documentation, images and API responses that are identical for everyone. It works poorly for an account, dashboard or API response containing personal data.

Vary: Cookie may look like a solution because the cache is instructed to separate responses using the cookie header. But this makes a sensitive security boundary depend on every cache variant being created and matched exactly as intended. At the same time, session and analytics values fragment the cache.

Vercel's new behaviour is therefore a clearer safeguard: if a response says that it varies by cookies, it is not treated as suitable for shared CDN storage. This reduces the risk of a personal response accidentally being handled as shareable content and avoids storing variants that are unlikely ever to be reused.

However, this does not mean that signed-in pages are automatically secure. The CDN rule does not replace authorisation checks in your app. The server must still check the session and ensure that the user is allowed to access the correct account, organisation, project and database rows. If you use Supabase, access should not be protected solely by what the interface displays. Database rules and server code must also restrict the user's access.

It is also important to distinguish between private and no-store in Cache-Control:

  • private indicates that the response must not be stored by a shared cache, but may allow private storage in the user's browser.
  • no-store instructs caches not to store the response at all and is appropriate when even local storage is unsuitable.
  • A public cache directive is only appropriate when the content can genuinely be shared between visitors.

For responses that do depend on cookies, Vercel recommends keeping Vary: Cookie and using Cache-Control: private. For particularly sensitive or rapidly changing responses, you may need a stricter policy, depending on how the application works.

What may happen to performance and costs

When a response is no longer retrieved from the CDN cache, the application must create or retrieve it again. This may involve running a Vercel Function, having server code verify a session and reading data from a service such as Supabase. The response time may then be longer than for a cached response, particularly if the database or an external service is in another region.

The load may also move further down the system. A route that previously reused stored responses may begin to cause more function executions, database queries and calls to external APIs. If your usage is billed according to these resources, the cost may be affected. The impact depends on traffic patterns, regions, database work and how much of the page is rendered on the server.

This only applies to the affected responses. Static files, images, JavaScript and other cacheable resources can still be delivered through Vercel's network. A signed-in page therefore does not necessarily become slow as a whole simply because the HTML response itself or a personal API call is not stored in the CDN cache.

A common unnecessary loss of performance occurs when a public route sends Vary: Cookie even though the content is identical for everyone. This can happen if the header is added globally in middleware, in a proxy or through a general server configuration. Visitors' analytics cookies may then give the impression that the page needs to vary, even though the application never reads them when building the content.

In that situation, Cookie should be removed from Vary, but only after you have verified that the response is genuinely independent of cookies. Check language, experiments, consent, currency, geographical adaptation and sign-in status, among other things. A small personal detail in server-rendered HTML is enough to make the response unsuitable for public sharing.

For genuinely personal pages, the absence of CDN caching is normally the right behaviour. Instead, optimise the dynamic path:

  • Retrieve only the data needed for the current view.
  • Add indexes for database queries that run frequently.
  • Avoid making independent external calls one after another.
  • Place functions and the database so that network latency remains reasonable.
  • Cache non-personal underlying data separately from the personal response.
  • Allow static parts of the interface to be built and delivered separately where the architecture permits.

Do not try to bypass the change by moving a unique user ID to a separate Vary header. This recreates the same problem under another name and may make the caching behaviour harder to understand.

How to review your app

Start with the routes where you expect CDN caching or where response times have recently changed. Test the deployed production environment, as local development does not show how Vercel's CDN actually handles the response.

Open the browser's network panel and inspect the response headers. Look in particular for:

  • Vary
  • Cache-Control
  • CDN-Cache-Control
  • Vercel-CDN-Cache-Control
  • x-vercel-cache
  • Set-Cookie

An isolated x-vercel-cache: MISS does not prove that Vary: Cookie is the reason. A normal miss can occur when the content is not yet in the cache and is then stored for future requests. You should therefore check Runtime Logs and look for vary_key_denied:cookie. This reason shows that the response was served but not written to the CDN cache.

Test while signed out, while signed in and with separate test accounts. If the product has organisations or workspaces, the accounts should belong to different ones. Compare both content and headers. The aim is to detect whether a route is personal without a clear private caching policy, or whether a public route unnecessarily varies by cookies.

Then review where the header is set. It may come from route code, a framework, middleware, an authentication solution, a proxy or a backend API forwarded by Vercel. Check the final response in production rather than only searching for the text in the codebase.

Also monitor changes in Vercel's observability tools and in your database. Look for changes in function executions, response times, database queries, external calls and cache outcomes. Compare representative periods and distinguish between public routes and signed-in routes. An increase may be entirely expected for personal content but may indicate an incorrect header on public pages.

A practical decision model

Use the content's actual behaviour as your starting point, not the desire for faster caching.

  • If the response is identical regardless of cookies, you can remove Cookie from Vary and then check that the other caching requirements are met.
  • If the response contains user-, account- or organisation-specific information, it must not be placed in a shared CDN cache. Keep the variation and set an explicit private caching policy.
  • If only a small part is personal, you can consider using a public, cacheable base and retrieving the private part through an authenticated API.
  • If the page sets a cookie but is otherwise public, check both Set-Cookie and the other cache headers, as this may also make the response unsuitable for CDN storage.
  • If you do not know why Vary: Cookie is present, do not simply remove it. First trace which code or service set the header and what behaviour it is intended to protect.

Finish the review with a clear checklist:

  • Public routes do not send Vary: Cookie without a genuine need.
  • Personal routes have an explicit private caching policy.
  • Authorisation is checked on the server and in the database.
  • Runtime Logs have been checked for vary_key_denied:cookie.
  • Separate accounts cannot access each other's data.
  • Functions, the database and external API calls are monitored after the change.
  • Static and public parts are kept separate from personal data where practical.

If the review shows that the prototype's caching, authentication and data access have become intertwined, the next step may be to move from prototype to product with clear boundaries between public and personal content.


Source: Vercel changelog

Want to know what your build would take?

Three questions, then you get a clickable sketch for free.

Tell me where you are
← All articles

More from Ketryon

Just need a page or a video?

Two standalone services for when a whole subscription is more than you need — a landing page or a launch video, made by the same person.