Next.js 13中设置Cookie后无法在服务端/客户端组件中获取的问题排查求助
我在Next.js v13开发认证功能时遇到了一个头疼的问题:我能成功把JWT Token存在Cookie里,但不管怎么尝试都没法在服务端组件和客户端组件里获取到它。我试了好几个Cookie处理库(比如cookie、js-cookie、cookies-next),甚至用了Next.js自带的next/headers里的cookies()方法,结果都是一样——要么拿到空对象、空数组,要么就是undefined。
登录接口代码(app/api/auth/loginUser/route.js)
import User from "@/app/_models/User"; import connectDB from "@/app/lib/connectdb"; import { AES, enc } from "crypto-js"; import { NextResponse } from "next/server"; import { sign } from "jsonwebtoken"; import { serialize } from "cookie"; const MAX_AGE = 60 * 60 * 24 * 2; export async function POST(request) { try { connectDB(); const { email, password } = await request.json(); const user = await User.findOne({ email: email }); if (!user) return new NextResponse( JSON.stringify({ success: false, message: "Invalid credentials", }) ); const dycPassword = AES.decrypt( user.password, process.env.CRYPT_SECRET_KEY ).toString(enc.Utf8); if (dycPassword !== password) { return new NextResponse( JSON.stringify({ success: false, message: "Invalid credentials", }) ); } const token = sign(user.toObject(), process.env.JWT_SECRET_KEY, { expiresIn: "2d", }); const setCookie = serialize("userToken", token, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "strict", maxAge: MAX_AGE, path: "/", httpOnly: false, // 这里重复设置了httpOnly,可能是笔误? }); return new NextResponse( JSON.stringify({ success: true, message: "Welcome!!!", }), { status: 200, headers: { "Set-Cookie": setCookie } } ); } catch (error) { return new NextResponse( JSON.stringify({ success: false, message: error.message }), { status: 500 } ); } }
获取用户Token的接口(app/api/auth/me/route.js)
import { cookies } from "next/headers"; import { NextResponse } from "next/server"; export async function POST(req) { try { const cookieStore = cookies(); const token = cookieStore.get("userToken"); console.log(cookieStore.getAll(), token); return new NextResponse( JSON.stringify({ success: true, token: token }, { status: 200 }) ); } catch (error) { return new NextResponse( JSON.stringify({ success: false, message: error.message }), { status: 500 } ); } }
首页服务端组件(app/page.jsx)
import React from "react"; import { cookies } from "next/headers"; export const dynamic = "force-static"; export default async function Page() { const token = cookies().get("userToken"); const data = await fetch("http://localhost:3000/api/auth/me", { method: "POST", }).then((data) => data.json()); console.log(data); return ( <div> <ul>{token}</ul> </div> ); }
项目依赖列表
"dependencies": { "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", "cookie": "^0.5.0", "crypto-js": "^4.1.1", "jsonwebtoken": "^9.0.2", "mongoose": "^7.5.3", "next": "latest", "next-themes": "^0.2.1", "react": "latest", "react-dom": "latest", "react-switch": "^7.0.0", "react-toastify": "^9.1.3", "sass": "^1.68.0" }, "devDependencies": { "autoprefixer": "latest", "eslint": "latest", "eslint-config-next": "latest", "postcss": "latest", "tailwindcss": "latest" }
后续排查更新
我后来研究了请求头,发现问题出在Cookie根本没传到需要的页面里:
- 用ThunderClient(VSCode的API测试插件)请求时,能正常拿到设置的Cookie;
- 但在Chrome和Edge浏览器里,请求头是空的,完全看不到Cookie的影子;
- 我写了个中间件打印请求头,中间件里能看到完整的请求内容(包括Cookie),但这些内容就是传不到服务端组件
app/page.js里。
中间件代码(middleware.js)
import { NextResponse } from "next/server"; export async function middleware(request) { console.log(request.headers.get("cookie")); return NextResponse.next({ request: { headers: new Headers(request.headers), }, }); } export const config = { matcher: "/", };
页面app/page.js中拿到的Headers:
HeadersList { cookies: null, [Symbol(headers map)]: Map(0) {}, [Symbol(headers map sorted)]: null }
接口app/api/auth/me/route.js中拿到的Headers:
HeadersList { cookies: null, [Symbol(headers map)]: Map(10) { 'accept' => { name: 'accept', value: '*/*' }, 'accept-encoding' => { name: 'accept-encoding', value: 'gzip, deflate' }, 'accept-language' => { name: 'accept-language', value: '*' }, 'cache-control' => { name: 'cache-control', value: 'no-cache' }, 'connection' => { name: 'connection', value: 'keep-alive' }, 'content-length' => { name: 'content-length', value: '0' }, 'host' => { name: 'host', value: 'localhost:3000' }, 'pragma' => { name: 'pragma', value: 'no-cache' }, 'sec-fetch-mode' => { name: 'sec-fetch-mode', value: 'cors' }, 'user-agent' => { name: 'user-agent', value: 'undici' } }, [Symbol(headers map sorted)]: [ [ 'accept', '*/*' ], [ 'accept-encoding', 'gzip, deflate' ], [ 'accept-language', '*' ], [ 'cache-control', 'no-cache' ], [ 'connection', 'keep-alive' ], [ 'content-length', '0' ], [ 'host', 'localhost:3000' ], [ 'pragma', 'no-cache' ], [ 'sec-fetch-mode', 'cors' ], [ 'user-agent', 'undici' ] ] }
登录后的跳转逻辑(表单提交函数)
const handleSubmit = async (e) => { e.preventDefault(); if (email == "" || password == "") { toast.error("Fill all the fields"); return; } const res = await fetch("api/auth/loginUser", { method: "POST", cache: "no-store", body: JSON.stringify({ email, password }), }).then((data) => data.json()); if (res.success) { toast.success(res.message); router.push("/"); // 登录成功后跳转到首页 } else { toast.error(res.message); } };
另外,我确认Cookie已经在浏览器里存储了,但就是没法在组件/接口里获取到。
备注:内容来源于stack exchange,提问作者Shashwat Gupta
相关产品推荐
相关产品推荐

