nextjs - 💡(How to fix) Fix Cross-version RSC navigation fails when two Next.js apps share a domain [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#92330Fetched 2026-04-08 02:43:26
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Author
Timeline (top)
closed ×1commented ×1labeled ×1locked ×1

Error Message

Current: The request returns a 500 Internal Server Error. The Next 16 server throws:

  • #91723 — Same error surface with Vercel Proxy matcher

Root Cause

We attempted to strip the RSC headers in middleware (on both the sender and receiver apps). This does not work because adapter.js explicitly force-restores all flight headers (rsc, next-router-state-tree, next-router-prefetch, next-url) after middleware returns:

Fix Action

Fix / Workaround

This means there is no Next.js-level mitigation available. The only workaround is stripping headers at the CDN/proxy layer (outside Next.js), which not all customers can easily do.

This affects any organization running multiple Next.js apps behind the same domain during a version migration — a common scenario for large teams incrementally upgrading their apps. The same-domain setup is typically required for shared auth, cookies, and SEO. The only current workarounds are:

Code Example

// adapter.js
// "Flight headers are not overridable / removable so they are applied at the end."
for (const [key, value] of flightHeaders) {
    finalResponse.headers.set(`x-middleware-request-${key}`, value);
    overwrittenHeaders.push(key);
}

---

Operating System:
  Platform: darwin
  Arch: arm64

Binaries:
  Node: 22.14.0
  npm: 10.9.2

Relevant Packages:
  Sender:  next@14.2.18, react@18.2.0
  Receiver: next@16.2.0, react@19.0.0

Deployed on Vercel using Turborepo monorepo.
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/nicdillon/rsc-cross-version-repro

Deployed reproduction:

To Reproduce

  1. Visit the sender app: https://rsc-cross-version-repro-next14-send.vercel.app
  2. Click "Bug: RSC Navigation (500)"
  3. Open DevTools → Network tab and observe the request to /receiver/no-fix?_rsc=...

Current vs. Expected behavior

Current: The request returns a 500 Internal Server Error. The Next 16 server throws:

The router state header was sent but could not be parsed.

Expected: Graceful handling — either a successful response or a clean fallback to full-page navigation, rather than a server-side 500.

What's happening

When two Next.js apps of different major versions (14 and 16) are served behind the same domain — a common pattern using CDN routing (CloudFront, Cloudflare), Vercel rewrites, or multi-zone setups — client-side <Link> navigations that cross the app boundary fail.

The Next 14 client router sends a Next-Router-State-Tree header on RSC fetches. The schema for this header changed between versions:

ElementNext 14Next 16
[2]optional(nullable(string()))optional(nullable(tuple([string, string])))
[3]"refetch" / "refresh""refetch" / "inside-shared-layout" / "metadata-only"
[4]optional(boolean())optional(number())

When the Next 16 server receives a Next 14 Next-Router-State-Tree, parseAndValidateFlightRouterState() fails superstruct validation and throws.

Why middleware can't fix this

We attempted to strip the RSC headers in middleware (on both the sender and receiver apps). This does not work because adapter.js explicitly force-restores all flight headers (rsc, next-router-state-tree, next-router-prefetch, next-url) after middleware returns:

// adapter.js
// "Flight headers are not overridable / removable so they are applied at the end."
for (const [key, value] of flightHeaders) {
    finalResponse.headers.set(`x-middleware-request-${key}`, value);
    overwrittenHeaders.push(key);
}

This means there is no Next.js-level mitigation available. The only workaround is stripping headers at the CDN/proxy layer (outside Next.js), which not all customers can easily do.

Customer impact

This affects any organization running multiple Next.js apps behind the same domain during a version migration — a common scenario for large teams incrementally upgrading their apps. The same-domain setup is typically required for shared auth, cookies, and SEO. The only current workarounds are:

  1. Strip RSC headers at the CDN/proxy layer before they reach the newer app
  2. Use plain <a> tags instead of <Link> for cross-app navigations (losing SPA transitions)
  3. Upgrade all apps to the same version simultaneously (often not feasible)

Possible improvements

  • Graceful fallback in parseAndValidateFlightRouterState(): Instead of throwing a 500, return undefined and fall back to a full-page HTML response. The client router already handles this fallback (we observed it recovering with a subsequent 200 document request after the 500).
  • Allow middleware to strip flight headers: Remove the force-restore behavior in adapter.js, or provide an opt-in mechanism to override flight headers in middleware. This would let teams add a middleware-based fix during version migrations.
  • Version negotiation: Include the Next.js version in RSC request/response headers so the server can detect a version mismatch and fall back gracefully.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64

Binaries:
  Node: 22.14.0
  npm: 10.9.2

Relevant Packages:
  Sender:  [email protected], [email protected]
  Receiver: [email protected], [email protected]

Deployed on Vercel using Turborepo monorepo.

Which area(s) are affected?

  • App Router
  • Middleware
  • Multi-zone / Rewrites

Which stage(s) are affected?

  • Production (Vercel deployment)

Additional context

Related issues:

  • #91723 — Same error surface with Vercel Proxy matcher
  • #67444 — RSC payload fails through third-party proxies
  • #40481 — Multi-zone relative routing challenges

extent analysis

TL;DR

The most likely fix for the issue is to strip the RSC headers at the CDN/proxy layer before they reach the newer app, or to use one of the other workarounds such as using plain <a> tags instead of <Link> for cross-app navigations.

Guidance

  • The issue is caused by the difference in the Next-Router-State-Tree header schema between Next 14 and Next 16, which causes the Next 16 server to throw a 500 error when receiving a request from a Next 14 client.
  • To verify the issue, check the request headers in the DevTools Network tab and observe the Next-Router-State-Tree header being sent by the Next 14 client.
  • One possible workaround is to use plain <a> tags instead of <Link> for cross-app navigations, which will lose SPA transitions but avoid the 500 error.
  • Another possible workaround is to upgrade all apps to the same version simultaneously, although this may not be feasible for large teams.

Example

No code snippet is provided as the issue is more related to the configuration and setup of the Next.js apps and the CDN/proxy layer.

Notes

The issue is specific to the setup where multiple Next.js apps of different major versions are served behind the same domain, and the workarounds may not be applicable to all scenarios.

Recommendation

Apply the workaround of stripping the RSC headers at the CDN/proxy layer, as this is the most feasible solution for teams that cannot easily upgrade all apps to the same version simultaneously.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING