Since April 6th, 2026, new Vercel projects have honored Cache-Control headers from external origins by default, inverting the caching contract for any backend served through a rewrite. Where the edge previously passed external-origin responses through uncached, the upstream now dictates its own TTL. If you front an S3 bucket, a legacy API, or another CDN through Vercel on a project created after that date, audit those headers or accept a hit-rate shift with no deploy.
What actually changed on April 6th?
For new Vercel projects created after April 6th, 2026, the CDN caches responses from external origins according to whatever Cache-Control, CDN-Cache-Control, and Vercel-CDN-Cache-Control headers the upstream returns, with no vercel.json opt-in required, per the changelog.
The scope is narrow but the semantics aren’t. This applies to rewrites to external origins, configured in the CDN tab of project settings, and only to projects created after the date. Existing projects keep their current behavior until an operator flips the opt-in from the project dashboard. That per-project gate is what kept the change from being a hard break for live traffic. New projects, new clones of a template, and anything spun up in a CI pipeline after the date all inherited the new default silently.
The changelog also notes that Vercel-Cache-Tag from origins can drive selective purges, and that x-vercel-enable-rewrite-caching set to 0 disables caching per path. Both become load-bearing once the origin controls the contract.
What was the old default, and why it matters now
Before this change, responses served through rewrites to external origins were uncached by default. Enabling caching required setting x-vercel-enable-rewrite-caching in vercel.json, per the changelog.
That prior default is the crux of the audit. A CDN that doesn’t cache your proxied backend is the safe, predictable state: every request hits the origin, and the edge is effectively a proxy. Two groups of operators have a problem now. The first assumed Vercel was caching their proxied content, because it is, after all, a CDN; it wasn’t, and the absence of caching was invisible. The second added x-vercel-enable-rewrite-caching years ago and forgot about it, so their caching was already origin-driven in practice. The first group’s hit rate jumps (or goes stale) on any project created since April 6th; the second sees little change.
A correction to the obvious framing: Vercel never applied one uniform caching policy to every backend. For external origins specifically, the uniform policy was “don’t cache unless explicitly enabled.” The flip is from uncached-by-default to origin-dictated-by-default.
Which headers should you audit first?
Check every external origin behind a rewrite for Cache-Control, CDN-Cache-Control, and Vercel-CDN-Cache-Control. The two CDN-specific headers give direct control over caching on Vercel and downstream CDNs, per the cdn-cache docs.
Those CDN-specific headers aren’t new. Vercel added CDN-Cache-Control and Vercel-CDN-Cache-Control for proxied responses in May 2025, per its earlier changelog, aligning external-backend caching with how Vercel Functions are cached. The April 6th change is the default flip that makes them load-bearing for new projects.
Two practical traps. An S3 bucket commonly emits Cache-Control: max-age=31536000 on objects, but a legacy proxy in front of it might emit Cache-Control: no-cache. Under the new default, the directives the origin returns are honored literally, so a no-cache from a legacy layer will suppress caching where you might have expected it. A backend that returns no caching header at all leaves nothing for the CDN to honor.
How do you opt out for specific paths?
Set x-vercel-enable-rewrite-caching to 0 on a per-path basis to disable caching for requests you don’t want Vercel to store, per the changelog.
The same header that previously enabled caching for external origins now also disables it when set to zero. Use it for authenticated endpoints, per-user responses, or anything that should never be stored regardless of what the origin says.
How does cache invalidation work?
Origins can attach Vercel-Cache-Tag headers to group cached responses for selective purge, letting you invalidate by tag rather than URL, per the changelog and the cache tags guide.
This is the primitive that makes origin-driven caching operable. Tag your objects at the origin, then purge by tag when upstream content changes. Without it, you are trusting the TTL to expire on its own, and with a long max-age and no purge, stale content is the cost. Once the origin owns the cache policy, cache tags are how it also owns invalidation.
What makes a response cacheable at all?
Honoring the origin’s header is necessary but not sufficient. Some directives explicitly suppress CDN caching. private prevents the CDN from caching a response, and no-store is the directive for content that must never be cached, per the cache-control headers docs.
A Cache-Control: max-age=3600 that also sets private is not cached, header notwithstanding. When auditing, watch for directives the origin’s auth layer adds: a response that looks cacheable by its max-age can still be excluded by a private or no-store it inherited.
What’s the realistic blast radius?
The hit-rate shift depends on what the origin actually emits. An external backend that returns no cacheable directive sees no change: the CDN has nothing to honor. An origin that returns a long max-age on a high-traffic path starts serving from cache, cutting origin load. The change only applies to projects created after April 6th or existing projects that have opted in from the dashboard, so the blast radius is bounded by how many of your projects fall into that set.
Vercel operates 126 Points of Presence across 51 countries, per its CDN overview. That coverage figure is platform-wide, not specific to this caching change.
What should operators do now?
What is new is Vercel-specific: the previous uncached-by-default stance for external origins is gone for new projects, and operators who internalized that behavior are the ones who need to re-audit. If your mental model of “Vercel fronts my backend” always assumed edge caching, the platform now matches that assumption for projects created since April 6th. If your model correctly assumed pass-through, expect it to have stopped being pass-through for any project created since that date.
The audit is mechanical: list every rewrite to an external origin, capture the actual response headers the origin emits today, and decide per-path whether the resulting TTL is what you want. The default now trusts your origin. Make sure your origin is trustworthy.
Frequently Asked Questions
Does the new default apply to Vercel Functions, or only to external origins?
It applies only to rewrites to external origins. Vercel Functions still ship with a default cache-control: public, max-age=0, must-revalidate response, so their caching behavior is unchanged unless you override it explicitly.
How does Vercel’s new behavior compare to Cloudflare or Fastly?
Most CDNs already honor origin Cache-Control for proxied backends by default, so Vercel is catching up rather than differentiating. Cloudflare and Fastly have treated upstream cache directives as the primary TTL signal for years, whereas Vercel previously required an explicit vercel.json opt-in.
What should teams monitor after opting in?
Track cache hit ratio and origin request volume, not just the declared TTL. Vercel’s cache times are best-effort; rarely requested assets can be evicted before their max-age expires, so a low-traffic endpoint may still hit the origin even when the header looks generous.
Can every proxied response be cached now?
No. Only GET and HEAD responses with specific status codes, 200/404/410/301/302/307/308, under 10 MB, and without Range, Authorization, Set-Cookie, or private/no-store/no-cache directives are eligible. A header alone does not make an otherwise non-cacheable response storeable.
What’s the main operational risk once the origin controls TTL?
A single misconfigured upstream header can now cache stale or private content across Vercel’s 126 PoPs. Because conflicting directives are not documented to resolve predictably, you should validate real origin responses in a staging project before enabling the new default in production.