New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Edge Functions: maxAge breaks Set-Cookie header #30430
Comments
It seems as if one of these is the root cause: next.js/packages/next/server/next-server.ts Line 1174 in 7902616
next.js/packages/next/server/web/utils.ts Line 42 in 82001f2
|
@styfle It seems as if this caused by #30288, specifically in d31615b. Splitting based on Since the middleware automatically sets next.js/packages/next/server/web/spec-extension/response.ts Lines 49 to 52 in 88131eb
Edit: Oh it's there because calling const headers = new Headers()
headers.append("set-cookie", "a")
headers.append("set-cookie", "b")
headers.get("set-cookie") // -> 'a, b' Unfortunately we still can't distinguish between header values genuinely containing References:
|
To sum up my findings: RFC 6265 says that the The header looks like This can also happen to other headers containing a |
Just to add some more context, the native web
Which include My guess is that CF workers implement their own version of They probably did not take the RFC above into consideration when "patching" those forbidden headers? Relevant issue: whatwg/fetch#973 Seems like that's why they proposed adding |
We could also look into utilizing this library which handles the splitting correctly: https://www.npmjs.com/package/set-cookie-parser#splitcookiestringcombinedsetcookieheader |
## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` Fixes #30430 There's some more discussion in the issue, but in summary: - web `Headers` implementation combines all header values with `', '` - For `Set-Cookie` headers, you're supposed to set them as separate values, not combine them - web `Headers` forbids the use of `Cookie`, `Set-Cookie` and some more headers, so they don't have custom implementation for those, and still joins them with `,` - We currently just split them using `split(',')`, but this breaks when the header contains a date (expires, max-age) that also includes a `,` I used this method to split the Set-Cookie header properly: https://www.npmjs.com/package/set-cookie-parser#splitcookiestringcombinedsetcookieheader as suggested [here](whatwg/fetch#973 (comment)) I didn't add it as a dependency, since we only needed that one method and I wasn't sure what the process is for adding dependencies, so I just added the method in the middleware utils
response.cookie was originally broken when Expires was present on the cookie: vercel/next.js#30430 This will be fixed in Next.js v12.0.2, so we use the canary of that for now, and the release of happykit might have to be a breaking change since we then no longer support older Next.js versions theoretically. We need to list next >=12.0.2 as a peerDependency once v12.0.2 is out.
response.cookie was originally broken when Expires was present on the cookie: vercel/next.js#30430 This will be fixed in Next.js v12.0.2, so we use the canary of that for now, and the release of happykit might have to be a breaking change since we then no longer support older Next.js versions theoretically. We need to list next >=12.0.2 as a peerDependency once v12.0.2 is out.
response.cookie was originally broken when Expires was present on the cookie: vercel/next.js#30430 This will be fixed in Next.js v12.0.2, so we use the canary of that for now, and the release of happykit might have to be a breaking change since we then no longer support older Next.js versions theoretically. We need to list next >=12.0.2 as a peerDependency once v12.0.2 is out.
* experiment: use from npm so we can use next 12 middleware * use local @happykit/flags * use @happykit/flags through workspace * use req.cookies on server * experimental middleware support * add middleware pages for all variants * move Content to components * Content -> EdgeFunctionContent * bring back dynamic recompliation of package upgrades @preconstruct/next to a version supporting Next.js 12 * use response.cookie response.cookie was originally broken when Expires was present on the cookie: vercel/next.js#30430 This will be fixed in Next.js v12.0.2, so we use the canary of that for now, and the release of happykit might have to be a breaking change since we then no longer support older Next.js versions theoretically. We need to list next >=12.0.2 as a peerDependency once v12.0.2 is out. * use getCookie * update dependencies * pre -> code * use differt header access methods * switch prebuild to preconstruct build * use next.js v12.0.2 * upgrade dependencies * add @tailwindui/react * upgrade @babel/preset-env * disable dynamic recompilation Did not work, so reverting back to triggering package builds manually. * add reload button to middleware * rename happykit.config to flags.config * increase maxAge * add flagBag.cookie to getEdgeFlags * avoid eval/new Function warning Middleware does not accept eval or new Function, but the way in which we create our custom errors uses that under the hood. So we need to throw native errors directly without extending the error class. * keep bottom bar on bottom even with short content like on the middleware demo page * list middleware support as key feature * add getEdgeFlags to README * remove InvalidConfigurationError * reenable preconstruct dev * Revert "reenable preconstruct dev" This reverts commit 2171933. * recreate cookieOptions object on every request This avoids vercel/next.js#31666 * explain middleware behaviour in EdgeFunctionContent * consistently use flags.config * make flagBag.cookie iterable * Revert "make flagBag.cookie iterable" This reverts commit 77bfb6f. * reword key features * remove jest-expect-message * add edge tests * v2.0.0
This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` Fixes vercel#30430 There's some more discussion in the issue, but in summary: - web `Headers` implementation combines all header values with `', '` - For `Set-Cookie` headers, you're supposed to set them as separate values, not combine them - web `Headers` forbids the use of `Cookie`, `Set-Cookie` and some more headers, so they don't have custom implementation for those, and still joins them with `,` - We currently just split them using `split(',')`, but this breaks when the header contains a date (expires, max-age) that also includes a `,` I used this method to split the Set-Cookie header properly: https://www.npmjs.com/package/set-cookie-parser#splitcookiestringcombinedsetcookieheader as suggested [here](whatwg/fetch#973 (comment)) I didn't add it as a dependency, since we only needed that one method and I wasn't sure what the process is for adding dependencies, so I just added the method in the middleware utils
What version of Next.js are you using?
12.0.0
What version of Node.js are you using?
16.3.0
What browser are you using?
Chrome, Firefox
What operating system are you using?
macOS
How are you deploying your application?
Vercel
Describe the Bug
When using
_middleware.ts
and setting a cookie viares.cookie(COOKIE_NAME, bucket, { maxAge: 10000 })
, the response contains two brokenSet-Cookie
headers instead of one correct one.Here is the response to the request in Chrome:
Notice how the first
set-cookie
header abruptly stops after "Wed" and the content that would follow there then follows in a separateSet-Cookie
header, except that the comma that should be after "Wed" is missing.Response headers as text
The value of
Set-Cookie
is originally something likebucket-marketing=original; Max-Age=10; Path=/; Expires=Wed, 27 Oct 2021 11:43:14 GMT
and it seems as if the,
splits the header into two.I tested, and this does not happen with other headers with the same value.
This seems to be a regression. The same code is working in
^11.1.3-canary.104
but not in12.0.0
.Expected Behavior
Only one
Set-Cookie
header is added to the response.To Reproduce
Clone the Simple AB Testing example and adapt:
Update the next.js version to v12 by editing
examples/edge-functions/ab-testing-simple/package.json
:Run
yarn
inexamples/edge-functions/ab-testing-simple
.Then edit
examples/edge-functions/ab-testing-simple/pages/marketing/_middleware
to:Run
yarn dev
to start Next.js.Open http://localhost:3000/marketing and inspect the response to the
marketing
site. You will see twoSet-Cookie
headers. This only happens whenmaxAge
orexpires
are set inres.cookie()
.The text was updated successfully, but these errors were encountered: