TL;DR
A logic flaw in the App Router’s middleware redirect path lets a React Server Component (RSC) response travel the same route and status code as the surrounding HTML document. Browsers happily cache the first payload they see, so later visitors may receive base64-encoded RSC data instead of a rendered page. Vercel’s managed infrastructure limits the blast radius to each user’s browser cache, but self-hosted deployments that sit behind a generic CDN can poison shared caches project-wide. Upgrade to Next.js 15.3.3 and Vercel CLI 42.2.0, then redeploy.
1. Summary
| CVE ID | CVE-2025-49005 |
| Affected Product(s) | Next.js App Router 15.3.0 through 15.3.2 and Vercel CLI 41.4.1 through 42.2.0 |
| Volerion Risk Score | 7.3 / 10 |
| Exploit Status | Reproduction steps public |
| CISA KEV | No |
The bug allows a malicious or simply unlucky request to prime the cache with an RSC payload. Subsequent visitors see JSON-like gibberish and base64 blobs instead of the fully rendered page. While this is not a direct code-execution issue, it damages availability, can leak snippets of server-side state and opens room for service-mix-up attacks.
2. Context – Why RSC versus HTML matters for caching
React Server Components ship server-side logic to the browser incrementally. They are streamed with the Content-Type: text/x-component header and rely on special request headers (RSC, Next-Router-State-Tree, Next-Router-Prefetch) to differentiate them from normal page requests.
Browsers and CDNs use the full request path and a set of vary headers to decide whether a stored response is reusable. If those headers do not differ between an RSC and an HTML navigation, the first response into the cache wins. The victim sees corrupted markup that the browser cannot parse, effectively converting a modern React site into a blank page.
3. Technical Details
The vulnerable code path sits in the App Router’s handling of middleware-triggered redirects. When a redirect response is generated, the router replays the original request internally. A misplaced condition removed the RSC header during the replay but kept the content-type detection logic unchanged. As a result:
- The internal fetch receives the
Accept: */*header instead oftext/x-component. - Next.js recognises the path as a server component route and streams RSC bytes.
- The framework copies status, location and most headers from the first response to the final one, but forgets to update
Content-Type, which remainstext/x-component. - The response leaves the server with a 200 status, no explicit vary line and the same URL that would normally serve HTML.
Any cache that keys on method + host + path now stores the RSC payload for all clients.
The fix located in commit ec202eccf05820b60c6126d6411fe16766ecc066 adds
+ res.headers.set("Vary", "RSC, Next-Router-State-Tree, Next-Router-Prefetch")
and tightens the type check so that redirected requests never downgrade the header set.
4. Impact
On Vercel’s managed platform the CDN already varies on RSC, so only the browser cache is poisoned. A single user will experience broken navigation until they clear their cache or the item ages out, typically after five minutes.
Self-hosted installs behind Cloudflare, Fastly, Nginx or an AWS ALB often do not distinguish between RSC and HTML out of the box. In those scenarios one malicious request can contaminate edge caches across regions. A marketing campaign or SEO crawler that hits the poisoned URL spreads the bad response to thousands of visitors, causing:
• Loss of availability because pages refuse to render
• Potential disclosure of internal component props, which may include feature flags or user-specific data
• Follow-on attacks such as response-smuggling when intermediaries treat the binary RSC stream as plain text
5. Remediation
Upgrade to:
• Next.js 15.3.3 or later
• Vercel CLI 42.2.0 or later
After upgrading, redeploy the application so the build output includes the patched router logic.
If immediate upgrade is impossible, add a middleware that sets
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch
on every RSC route. This forces caches to store separate variants for component streams and full HTML pages.
6. Timeline
| Date (UTC) | Milestone |
|---|---|
| 2025-07-03 21:15 | CVE-2025-49005 published on GitHub Security Advisories |
| 2025-07-03 21:18 | Volerion completes enrichment and publishes risk score |
7. References
- GitHub advisory: https://github.com/vercel/next.js/security/advisories/GHSA-r2fc-ccr8-96c4
- Patch commit: https://github.com/vercel/next.js/commit/ec202eccf05820b60c6126d6411fe16766ecc066
- Issue discussion and reproduction steps: https://github.com/vercel/next.js/issues/79346
- Release notes for 15.3.3: https://github.com/vercel/next.js/releases/tag/v15.3.3
- Vercel changelog entry: https://vercel.com/changelog/cve-2025-49005
About Volerion
Volerion delivers AI-driven enrichment minutes after a CVE goes live. A single call to our REST API returns CVE metadata, CVSS 3.x and 4.x vectors, exploitability metrics and affected product information complete with remediation guidance. We also calculate the Volerion Risk Score, an aggregate measure that blends eight separate dimensions such as exploit maturity, prevalence and patch effort.
At the time of writing:
- CVSS 3.1 rates CVE-2025-49005 at 7.5, reflecting the high integrity impact of poisoning shared caches.
- EPSS assigns a score of 0.0000, indicating no observed exploitation signals yet.
- The Volerion Risk Score is 7.3, placing the flaw in our high-risk band because the technical barrier to exploitation is low and the potential business disruption is significant, even though no in-the-wild attacks have surfaced so far.