fix: prevent duplicate Location and x-nextjs-stale-time headers on redirect#92057
Open
claygeo wants to merge 3 commits intovercel:canaryfrom
Open
fix: prevent duplicate Location and x-nextjs-stale-time headers on redirect#92057claygeo wants to merge 3 commits intovercel:canaryfrom
claygeo wants to merge 3 commits intovercel:canaryfrom
Conversation
When a malformed request body is sent to a server action endpoint (e.g. by vulnerability scanners probing for CVE-2025-55182), decodeReply throws a SyntaxError that bubbles up to the generic catch handler and returns HTTP 500. This is incorrect — a malformed client request should return 400 Bad Request, not 500 Internal Server Error. Wrap the decodeReply calls in both the edge/web runtime and node runtime text body paths with try/catch for SyntaxError. Only SyntaxError is caught and mapped to 400; all other errors are re-thrown to preserve existing error handling behavior. Fixes vercel#86945
…h middleware rewrites When a server action fires during navigation, Next.js forwards the action to the correct worker. If middleware applies a rewrite to the forwarded request, the rewritten request arrives at a worker that also tries to forward, creating an infinite loop. The x-action-forwarded header is already set on forwarded requests (createForwardedActionResponse, line 221) and read on incoming requests (line 711), but the forwarding guard at line 713 only checks actionId without checking whether the action was already forwarded. Add the !actionWasForwarded condition to prevent re-forwarding. Fixes vercel#84504
…direct When a page redirect is rendered, the render phase sets Location via setHeader on the response. Then the cache serving code in app-page.ts re-applies cached headers using the native appendHeader, which appends unconditionally without checking for existing values. This produces duplicate Location headers (e.g. Location: /redirect, Location: /redirect). Behind Cloudflare or similar proxies, duplicated Location headers get merged into "Location: /redirect, /redirect" which is an invalid redirect target, breaking navigation entirely. Use setHeader (replace) instead of appendHeader (add) for headers that don't support multiple values. Keep appendHeader for set-cookie, vary, www-authenticate, and proxy-authenticate which are multi-value by spec. Fixes vercel#82117
Collaborator
|
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #82117
Redirect responses include duplicate
Locationandx-nextjs-stale-timeheaders. Behind Cloudflare or similar proxies that merge duplicate headers,Location: /redirectbecomesLocation: /redirect, /redirect, which is an invalid redirect target that breaks navigation entirely.Regression introduced in 15.4.1. Multiple users confirmed; reverting to 15.4.0 resolves it.
Root Cause
Two code paths set the same headers on the same response object:
Source 1 (render phase):
app-render.tsx:3482callssetHeader('location', redirectUrl)which sets the header on theNodeNextResponseand stores it inmetadata.headersfor caching.Source 2 (cache serving):
app-page.ts:1611-1624iteratescachedData.headersand callsres.appendHeader(key, value)using the nativeServerResponse.appendHeader, which unconditionally appends without checking for existing values.Fix
In the cache header application loop (
app-page.ts:1611-1624), usesetHeader(replace) instead ofappendHeader(add) for headers that don't support multiple values. KeepappendHeaderfor headers that are multi-value by HTTP spec:set-cookie,vary,www-authenticate,proxy-authenticate.This matches the same multi-value allowlist used in
send-response.ts:35-41.