fix: trigger MPA navigation for server action redirects with build ID mismatch#89946
fix: trigger MPA navigation for server action redirects with build ID mismatch#89946
Conversation
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
Tests Passed |
Stats from current PR🟢 1 improvement
📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles: **402 kB** → **401 kB** ✅ -745 B80 files with content-based hashes (individual files not comparable between builds) Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📝 Changed Files (25 files)Files with changes:
View diffsapp-page-exp..ntime.dev.jsfailed to diffapp-page-exp..time.prod.jsfailed to diffapp-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsfailed to diffapp-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsfailed to diffapp-page.runtime.dev.jsfailed to diffapp-page.runtime.prod.jsfailed to diffapp-route-ex..ntime.dev.jsDiff too large to display app-route-ex..time.prod.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..time.prod.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..time.prod.jsDiff too large to display app-route.runtime.dev.jsDiff too large to display app-route.ru..time.prod.jsDiff too large to display pages-api-tu..ntime.dev.jsDiff too large to display pages-api-tu..time.prod.jsDiff too large to display pages-api.runtime.dev.jsDiff too large to display pages-api.ru..time.prod.jsDiff too large to display pages-turbo...ntime.dev.jsDiff too large to display pages-turbo...time.prod.jsDiff too large to display pages.runtime.dev.jsDiff too large to display pages.runtime.prod.jsDiff too large to display server.runtime.prod.jsDiff too large to display 📎 Tarball URL |
4198d47 to
2f45f98
Compare
… mismatch When a server action calls redirect() and the redirect target is served by a different Next.js zone (separate Vercel project on same domain), the server pre-fetches the redirect target as RSC. In a multi-zone setup, this RSC response comes from a different build with a different build ID. Previously, the server-action-reducer did not check the build ID of the action response before applying the RSC payload. This caused a blank page because the client tried to use RSC data from a foreign build. This fix adds a build ID check to fetchServerAction(), matching the pattern already used in fetch-server-response.ts for regular navigations. When a build ID mismatch is detected, the flight data is discarded and the redirect falls through to completeHardNavigation(), which triggers an MPA navigation (full page load) — the correct behavior for cross-zone redirects.
Extends the existing deployment-skew test to verify that when a server action calls redirect() and the target is served by a different deployment (different build ID), the client performs an MPA navigation instead of a client-side transition.
…ment ID handling - Hoist actionResult and couldBeIntercepted assignments above the build ID check since they're identical in both branches - Update the comment in app-page.ts that incorrectly stated 'the client doesn't check the build ID for action responses' — it now does. The exclusion of server actions from the deployment ID header is still correct because action redirect responses get the header from the pre-fetched redirect target (via createRedirectRenderResult in action-handler.ts).
Two issues fixed: 1. Set __NEXT_TEST_MODE in custom builds so the __NEXT_HYDRATED callback is included. Without this, webdriver() hydration detection times out and the test clicks the button before React hydrates, causing 'Router action dispatched before initialization'. 2. Inject a foreign x-nextjs-deployment-id header on POST responses via the proxy's proxyRes handler. This simulates a CDN reporting a different deployment ID, triggering client-side mismatch detection which discards flight data and falls back to MPA navigation.
3c32760 to
991ede0
Compare
… mismatch (#89946) ## Summary - Fixes cross-zone server action redirect failure where `redirect()` inside a server action produces a blank page when the target path is served by a different Next.js zone (separate Vercel project, same domain) - Adds build ID validation to `server-action-reducer.ts`, matching the existing pattern in `fetch-server-response.ts` - Adds e2e test in the deployment-skew test suite to verify server action redirects with build ID mismatch trigger MPA navigation ## Background When a server action calls `redirect()`, the server pre-fetches the redirect target and includes the RSC data in the response. In a multi-zone setup (same domain, different Next.js projects), this pre-fetched RSC data comes from a different build with a different build ID. The `fetch-server-response.ts` file already checks for build ID mismatches during regular navigations (line 245-251) and triggers MPA navigation when detected. However, `server-action-reducer.ts` was missing this check, causing the client to try to apply the foreign RSC payload — resulting in a blank page. ## Fix Added a build ID check in `fetchServerAction()` after parsing the RSC response. When a mismatch is detected, the flight data is discarded. The existing reducer logic at line 364-370 already handles this case: ```typescript if (flightData === undefined && redirectLocation !== undefined) { return completeHardNavigation(state, redirectLocation, navigateType) } ``` This triggers an MPA navigation (full page load), which is the correct behavior for cross-zone redirects. ## Repro - https://repro-main-app.vercel.app/test-redirect - Red button (server action redirect) → blank page (before fix) / full page load (after fix) - Green link (normal `<a>`) → works fine Closes https://linear.app/vercel/issue/NEXT-4856 --------- Co-authored-by: Zack Tanner <1939140+ztanner@users.noreply.github.com>
Summary
redirect()inside a server action produces a blank page when the target path is served by a different Next.js zone (separate Vercel project, same domain)server-action-reducer.ts, matching the existing pattern infetch-server-response.tsBackground
When a server action calls
redirect(), the server pre-fetches the redirect target and includes the RSC data in the response. In a multi-zone setup (same domain, different Next.js projects), this pre-fetched RSC data comes from a different build with a different build ID.The
fetch-server-response.tsfile already checks for build ID mismatches during regular navigations (line 245-251) and triggers MPA navigation when detected. However,server-action-reducer.tswas missing this check, causing the client to try to apply the foreign RSC payload — resulting in a blank page.Fix
Added a build ID check in
fetchServerAction()after parsing the RSC response. When a mismatch is detected, the flight data is discarded. The existing reducer logic at line 364-370 already handles this case:This triggers an MPA navigation (full page load), which is the correct behavior for cross-zone redirects.
Repro
<a>) → works fineCloses https://linear.app/vercel/issue/NEXT-4856