fix: deduplicate Set-Cookie headers from middleware + server actions#92727
Open
claygeo wants to merge 1 commit intovercel:canaryfrom
Open
fix: deduplicate Set-Cookie headers from middleware + server actions#92727claygeo wants to merge 1 commit intovercel:canaryfrom
claygeo wants to merge 1 commit intovercel:canaryfrom
Conversation
…s set the same cookie When middleware sets a cookie and a server action also sets the same cookie, the response contained duplicate Set-Cookie headers for the same cookie name. This violated RFC 6265 and could cause 502 errors when response headers exceeded proxy limits (8KB on AWS/Akamai). The fix deduplicates Set-Cookie headers by cookie name in send-response.ts, which is the final gateway before the response is sent. The last value for each cookie name wins, preserving the correct execution order (server action after middleware). Fixes vercel#69785
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 |
1 similar comment
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 |
Comment on lines
+73
to
+77
| const existingSetCookie = res.getHeader('set-cookie') | ||
| if (existingSetCookie) { | ||
| const cookieArray = Array.isArray(existingSetCookie) | ||
| ? (existingSetCookie as string[]) | ||
| : [String(existingSetCookie)] |
Contributor
There was a problem hiding this comment.
Suggested change
| const existingSetCookie = res.getHeader('set-cookie') | |
| if (existingSetCookie) { | |
| const cookieArray = Array.isArray(existingSetCookie) | |
| ? (existingSetCookie as string[]) | |
| : [String(existingSetCookie)] | |
| const existingSetCookie = res.getHeaderValues('set-cookie') | |
| if (existingSetCookie) { | |
| const cookieArray = existingSetCookie |
Set-Cookie deduplication code uses res.getHeader() which returns a comma-joined string, making the deduplication logic completely ineffective.
| await browser.deleteCookies() | ||
| }) | ||
|
|
||
| it('should not produce duplicate Set-Cookie headers when middleware and server action set the same cookie', async () => { |
Contributor
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.
Summary
Fixes #69785
When middleware and a server action both set the same cookie (e.g.,
shared-cookie), the response contains duplicateSet-Cookieheaders for that cookie name. This violates RFC 6265 Section 4.1.2 and can cause 502 errors when response headers exceed proxy limits (8KB on AWS ALB/Akamai).Root Cause
Middleware cookies flow through two independent paths to the final response:
onUpdateCookiesviamergeMiddlewareCookies): When the page/action callscookies(), middleware cookies are merged into the mutable cookie store, andres.setHeader('Set-Cookie', ...)replaces all Set-Cookie headers (correct, deduplicates)send-response.tsappendHeader): The Response object's headers (which also contain middleware cookies viaappendMutableCookies) are appended tores, creating duplicates of what Path A already wroteFix
After copying all response headers to
resinsend-response.ts, deduplicate Set-Cookie headers by cookie name. The last value for each name wins (server action over middleware), using a simpleMap<string, string>keyed bycookie.split('=')[0].Changes
packages/next/src/server/send-response.ts: Add Set-Cookie dedup block after header copying looptest/e2e/app-dir/app-middleware/: Add regression test with middleware + server action setting overlapping cookiesTest Plan
/cookie-dedup)x-middleware-set-cookieinternal header not leaked