next.js: [NEXT-1126] Cookies set in middleware missing on Server Component render pass
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 22.3.0: Thu Jan 5 20:48:54 PST 2023; root:xnu-8792.81.2~2/RELEASE_ARM64_T6000
Binaries:
Node: 18.13.0
npm: 8.19.3
Yarn: N/A
pnpm: 7.25.0
Relevant packages:
next: 13.4.1
eslint-config-next: 13.1.6
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Middleware / Edge (API routes, runtime)
Link to the code that reproduces this issue
https://codesandbox.io/p/sandbox/exciting-tamas-jhekzr
To Reproduce
- Load the URL in your browser
- Check logs. It should log a UUID
newSessionId
frommiddleware.ts
, but undefinedsessionId
fromlayout.tsx
. - If you refresh the page,
sessionId
is now picked up correctly and logged out fromlayout.tsx
.
Describe the Bug
Cookies set in middleware only show up in Server Components from the first request after it was set.
Expected Behavior
I would expect a cookie set in middleware, to be available in Server Components in the render pass in which it was set.
Which browser are you using? (if relevant)
Version 112.0.5615.137 (Official Build) (arm64)
How are you deploying your application? (if relevant)
This happens both locally and on our Vercel preview environments
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 49
- Comments: 54 (2 by maintainers)
This happens because cookies are read from the
Cookies
header on the request, whereas you set cookies using theSet-Cookie
header on the response. For a single given page load, Middleware and Server Components executed as part of a single request/response cycle. When it receives aSet-Cookie
header on a response, the browser can’t then go back in time to put a differentCookie
header on the request it already sent (the reason theredirect
workaround works is that it creates a second request, which will carry the updatedCookie
header).For cookies set in middleware, you can work around this problem using the mechanism added in https://github.com/vercel/next.js/pull/41380 — this allows middleware to “override” what Next will tell Server Components about the value of the
Cookie
header on the request. Overriding theCookie
header will make Server Component functions likecookies()
return different values, even for the same request.The following is an
applySetCookie(req, res)
function that you can drop in to your existing middlewares, which will make thecookies()
function in RSC reflect the newly-set cookies as expectedExample usage
middleware.ts
app/page.tsx
Yeah I’m having the exact same issue.
Setting cookies for the first response is pretty critical for most web apps I’d imagine (i.e. session IDs), so hopefully there’s a way to make this work. I assume we can’t set cookies in the Server Component itself because Next has already begun streaming the response by the time it runs?
A hacky workaround for now: use a redirect to trigger a new request with the cookie attached
This is obviously not a great solution though since it adds a whole extra request for any visitors without the cookie (and could trigger an infinite loop if their browser blocks cookies).
@timneutkens with the App Router having settled at stable somewhat, might we ask you to reconsider the priority on this? It’s still a huge problem for us, and it seems like others in the thread here have a similar view on the criticality of this issue. I’m not trying to antagonize, just kindly asking to reconsider or let us know what we can expect here. Thanks ❤️
we’re gonna look at tackling this soon, thank you for your patience
Please be patient, there’s hundreds of open issues and this one is not the top priority over performance/memory issues and such. The issue is in our project for App Router followups.
@StevenLangbroek Fair, again I share your frustration. Several fairly standard and simple use cases in middleware involve setting a cookie that needs to be accessed inside a server component that should be covered by docs:
It would be great to get an official voice on how to do it w/o relying on the community to figure it out.
For other folks reading this, please still test out the solution I posted above. I would like to get external validation that it works.
I’m interfacing with an API that I have no control over, but the expiry is set to 14 days, so @oliverjam’s redirect workaround (https://github.com/vercel/next.js/issues/49442#issuecomment-1538691004) is good enough for me and seems to be working well. I’d rather an awkward redirect instead of an error page once a fortnight.
Disappointing that something as basic as token refreshing has been essentially broken for almost a year now.
This is still not working, I guess the redirect option is the only working solution. Its 2024, I expected a working solution for a basic use case like this before calling it stable.
You could also try this workaround that doesn’t require any changes to middleware.
None of the workarounds work when deployed to cloud env, esp. when the RSC is on
runtime = 'nodejs'
and the middleware is on Edge (as it is always on Vercel).The
headers()
andcookies()
helper magic methods are supposed to return only the REQUEST headers/cookies, whileset-cookie
is a response header.Workaround (Vercel-friendly and validated)
The “correct” way of thinking and working around that limitation is to do one of the two things:
In middleware, set coockie works localy (both in dev and prod) without any fix or workaround. But when in Vercel deployement it still doesn’t work even with the fix :
Any idea when this will be fixed please ?
Another +1 here, the issue isn’t strictly with Server Components, page routing has the same problems.
It’s not uncommon to need to ensure the presence of a cookie value such as session ID’s
Hi,
I attempted your fix and after the redirect i’m still not seeing the cookie in the front end. Which redirects me back to the login. Only when the application is deployed though, it doesn’t happen when using it locally.
Does anyone still encounter this issue?
Issue also applies to
headers
set onNextResponse.next()
after construction:I can’t pass these to the
NextReponse.next
static method, asnext-intl
is in charge of that.edit: I was testing this incorrectly,
headers
work actually, apologies for any confusion. the issue with cookies remains.Sorry I accidentally deleted a reply of mine, but just to reiterate: your solution was mentioned before, and did / does not work on production.
Honestly, I’ve moved on from the project where I need this, and have stopped using and recommending NextJS or Vercel. If the company acting as its steward can’t prioritize a bugfix over [insert swanky AI feature], I’m putting my teams and their commitments at risk.
@controversial @andre-muller I think I finally found out the fix which took me too many hours.
https://nextjs.org/docs/app/api-reference/functions/next-response#next
Cookies are read-only in server components and allow you to read incoming HTTP request cookies. However, when you set-cookie, it modifies the set-cookie header on the HTTP response.
For the incoming request to contain your newly set cookie, you can add it to the incoming request.
Even though it took me many hours, I would ask the NextJS team to make this more clear in the documentation. The pattern is quite unintuitive.
Yeah when using locally i can see the header, but when i deploy it is not working. @controversial Do you have any idea about it ?
I appreciate and value that effort, I’m just no longer interested in contributing, given the lack of interest by the owner of the project and the fact that I’m not currently using it. I’m sure others will gladly help you test your solution. Good luck!
The feature to override request headers from Middleware is described in the docs as part of the official API, so it should be stable, but of course “should be” != “is”
The workaround I posted worked at the time that I wrote it, but it’s possible that this feature has changed/broken in more recent Next.js releases.
@controversial’s workaround works in dev mode but not in the prod build. I’m using pages router with Next v14.0.4. Cookies set on request or response don’t appear in the
getServerSideProps
in prod build.I’m sorry, friends. I’d like to set the record straight. According to the documentation we can only set or delete cookies in the
What should I do if I want to handle a normal simple authorization case? Let’s say, in the server component, I realize that the jwt token that is stored in the cookie is out of date (it could be a 401 response when receiving data or just checking the token and realizing it’s old) and I want to delete it. How do I do it correctly? According to the documentation, you can’t delete the cookie in the server component. I could delete it in middleware but that doesn’t give the proper result either. I’ve been trying to solve this problem for quite a while now, but I haven’t figured out how the nextjs team recommends doing it. I would be grateful for any information or help.
@timneutkens this is starting to block our work, is there any estimate attached to the Linear issue? The suggested workaround doesn’t work for us. If I construct
ResponseCookies
myself with the return value ofheaders()
, it’s entirely empty.