Setting cookies in middleware and reading in server components #50374
Replies: 5 comments 5 replies
-
Have you solved this issue? There are a multitude of posts on the same issue which would be simple in any other framework; none of them have a solution as of yet :/. |
Beta Was this translation helpful? Give feedback.
-
Edit: see my second answer for a better solution While middleware.ts: import { NextResponse } from 'next/server';
export default () => {
const res = NextResponse.next();
res.cookies.set('foo', 'bar');
return res;
} app/test/page.tsx: import React from 'react';
import { headers, cookies } from 'next/headers';
export default function Page() {
console.log(cookies().get('foo'));
console.log(headers().get('set-cookie');
return null;
} If you visit the
which shows that headers() sees the middleware-updated cookies, even while cookies() cannot. I’m using the set-cookie-parser package to read values from this string. Based on @sanderpick’s answer in my earlier discussion #38650 |
Beta Was this translation helpful? Give feedback.
-
A better approach: the A mechanism to modify request headers from middleware was added in #41380. Note this solution relies somewhat on undocumented Next.js internals middleware.ts: import { NextResponse, type NextRequest } from 'next/server';
import { ResponseCookies, RequestCookies } from 'next/dist/server/web/spec-extension/cookies';
/**
* Copy cookies from the Set-Cookie header of the response to the Cookie header of the request,
* so that it will appear to SSR/RSC as if the user already has the new cookies.
*/
function applySetCookie(req: NextRequest, res: NextResponse) {
// 1. Parse Set-Cookie header from the response
const setCookies = new ResponseCookies(res.headers);
// 2. Construct updated Cookie header for the request
const newReqHeaders = new Headers(req.headers);
const newReqCookies = new RequestCookies(newReqHeaders);
setCookies.getAll().forEach((cookie) => newReqCookies.set(cookie));
// 3. Set up the “request header overrides” (see https://github.com/vercel/next.js/pull/41380)
// on a dummy response
// NextResponse.next will set x-middleware-override-headers / x-middleware-request-* headers
const dummyRes = NextResponse.next({ request: { headers: newReqHeaders } });
// 4. Copy the “request header overrides” headers from our dummy response to the real response
dummyRes.headers.forEach((value, key) => {
if (key === 'x-middleware-override-headers' || key.startsWith('x-middleware-request-')) {
res.headers.set(key, value);
}
});
}
export default function middleware(req: NextRequest) {
// Set cookies on your response
const res = NextResponse.next();
res.cookies.set('foo', 'bar');
// Apply those cookies to the request
applySetCookie(req, res);
return res;
} page.tsx: import React from 'react';
import { headers, cookies } from 'next/headers';
export default function Page() {
console.log(cookies().get('foo')); // works now!
return null;
} |
Beta Was this translation helpful? Give feedback.
-
I created a library that helps define a refresh token in cookies through middleware and uses the mechanism that @controversial colleague mentioned. |
Beta Was this translation helpful? Give feedback.
-
Just set it in response :
|
Beta Was this translation helpful? Give feedback.
-
I'm not sure if this is a bug or intended behaviour, either way it would be good to validate my approach and get some ideas on how to accomplish the following:
The question is how to read the response cookie in a server component?
I don't think this is possible and I kinda suspect this is by design.
I tried to mutate the request cookies in middleware (not sure if they are immutable, but, again, suspect that they are by design). The cookies/headers functions from
next/headers
uses some sort of async storage, which makes sense, but, does this have a copy of the original request? i.e. I am mutating the request (without errors) but that gets dumped and then the server components reads thecookies()
from the async storage which is a different instance of the request headers/cookies?I think this issue for getServerSideProps might be the same thing, the key difference being that I don't think there is any way for server components to read the response. (I will try the redirect method mentioned there—redirect to the same page, effectively first request refreshes cookies, then the redirect proceeds as normal with fresh cookies—which is fine, but doesn't feel like a "good" solution).
I think this all distills to two problems:
Beta Was this translation helpful? Give feedback.
All reactions