You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vercel部署Next.js14与Express API时Cookie设置失败问题求助

Vercel部署Node/Express + Next.js 14.1.0时的Cookie异常问题

输入图片描述

已将Node/Express API与Next.js 14.1.0客户端部署至Vercel,其他功能均正常,但Cookie存在两类异常问题:

  • 部分场景下能获取到Cookie,但仅为临时有效,刷新页面后便会丢失;
  • 另一些场景则会出现如下关于域名的警告:

"尝试通过Set-Cookie设置Cookie被阻止,因其domain属性无效"

即使按如下代码为Cookie设置domain,问题仍未解决:

res.cookie("refreshToken", refreshToken, {
    maxAge: 15 * 24 * 60 * 60 * 1000,
    sameSite: "none",
    httpOnly: true,
    secure: true,
    partitioned: true,
    domain: ".mern-nextjs-ecommerce.vercel.app"
  });
  res.cookie("accessToken", accessToken, {
    maxAge: 15 * 24 * 60 * 60 * 1000,
    sameSite: "none",
    httpOnly: true,
    secure: true,
    partitioned: true,
    domain: ".mern-nextjs-ecommerce.vercel.app"
  });

登录后端代码

export const signIn = TryCatch(async (req, res, next) => {
  const { email, password } = req.body;

  const user = await User.findOne({ email });
  if (!user) return next(new ErrorHandler("Invalid credentials", 400));

  const isMatched = await user.comparePassword(password);
  if (!isMatched) return next(new ErrorHandler("Invalid credentials", 400));

  const accessToken = jwt.sign({ id: user._id }, config.JWT_SECRET, {
    expiresIn: "45m",
  });

  const refreshToken = jwt.sign({ id: user._id }, config.JWT_SECRET);

  if (!user.tokens) user.tokens = [refreshToken];
  else user.tokens.push(refreshToken);

  await user.save();

  res.cookie("refreshToken", refreshToken, {
    maxAge: 15 * 24 * 60 * 60 * 1000,
    sameSite: "none",
    httpOnly: true,
    secure: true,
    partitioned: true,
  });
  res.cookie("accessToken", accessToken, {
    maxAge: 15 * 24 * 60 * 60 * 1000,
    sameSite: "none",
    httpOnly: true,
    secure: true,
    partitioned: true,
  });

  res.json({
    success: true,
    profile: {
      id: user._id,
      email: user.email,
      name: user.username,
      verified: user.verified,
      avatar: user.avatar?.url,
      accessToken: accessToken,
    },
    accessToken,
    refreshToken,
  });
});

CORS配置

app.use(
  cors({
    origin: [
      "https://mern-nextjs-ecommerce.vercel.app",
      config.ADMIN_CLIENT_URL,
      "http://localhost:3000",
    ],
    credentials: true,
    methods: ["GET", "POST", "PUT", "DELETE"],
    allowedHeaders: [
      "Content-Type",
      "Authorization",
      "Origin",
      "X-Requested-With",
      "Accept",
      "Set-Cookie",
      "Cookie",
    ],
  })
);

前端登录提交函数

const handleSignInSubmit = async (data: SignInInput) => {
    try {
      const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/signin`, {
        method: "POST",
        credentials: "include",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          email: data.email,
          password: data.password,
        }),
      });

      const resData = await res.json();

       toast.success(resData.message || "Logged In !");
    } catch (error) {
      toast.error("An error occurred");
    }
  };

middleware.ts代码

import { NextRequest, NextResponse } from "next/server";

export async function middleware(request: NextRequest) {
  const accessToken = request.cookies.get("accessToken")?.value;
  const refreshToken = request.cookies.get("refreshToken")?.value;

  console.log("accessToken: ", accessToken);
  console.log("refreshToken: ", refreshToken);

  const response = NextResponse.next();

  if (!accessToken || !refreshToken) return response;

  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/me`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
    credentials: "include",
  });
  if (res.status === 401) {
    try {
      const res = await fetch(
        `${process.env.NEXT_PUBLIC_API_URL}auth/refresh-token`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            refreshToken: refreshToken,
          }),
          credentials: "include",
        }
      );

      const data = await res.json();
      if (data.success) {
        const response = NextResponse.redirect(request.url);
        response.cookies.set("accessToken", data.tokens.access, {
          httpOnly: true,
          sameSite: "lax",
        });
        response.cookies.set("refreshToken", data.tokens.refresh, {
          httpOnly: true,
          sameSite: "lax",
        });
        return response;
      }
    } catch (error) {
      console.log("An error occurred11111: ", error);
    }
  }

  return response;
}

前端注册提交函数

const handleSignInSubmit = async (data: SignUpInput) => {
    try {
      const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/signup`, {
        method: "POST",
        credentials: "include",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          username: data.username,
          email: data.email,
          password: data.password,
        }),
      });

      const resData = await res.json();

      if (res.ok) {
        if (resData.success) {
          setOpen(false);
          router.refresh();
        }
      } else {
        setOpen(false);
        toast.error(resData.message || "An error occurred");
      }
    } catch (error) {
      toast.error("An error occurred");
    }
  };

注册后端代码

export const createNewUser = TryCatch(async (req, res, next) => {
  const { username, email, password } = req.body;

  const isUserThere = await User.findOne({ email });

  if (isUserThere) {
    return next(new ErrorHandler("User already exists", 400));
  }

  const user = await User.create({
    username,
    email,
    password,
  });

  const token = crypto.randomBytes(36).toString("hex");
  await AuthVerificationTokenModel.create({ userId: user._id, token });

  console.log(token);

  const accessToken = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
    expiresIn: "5m",
  });

  const refreshToken = jwt.sign({ id: user._id }, process.env.JWT_SECRET);

  const link = `${process.env.CLIENT_URL}verify?token=${token}&id=${user._id}`;

  await sendEmail(user.email, link);

  if (!user.tokens) user.tokens = [refreshToken];
  else user.tokens.push(refreshToken);

  await user.save();

  res.cookie("refreshToken", refreshToken, {
    maxAge: 15 * 24 * 60 * 60 * 1000,
    sameSite: "none",
    httpOnly: true,
    secure: true,
  });

  res.cookie("accessToken", accessToken, {
    maxAge: 15 * 24 * 60 * 60 * 1000,
    sameSite: "none",
    httpOnly: true,
    secure: true,
  });

  res.status(201).json({
    success: true,
    message: "Registration successful | Please verify your email",
    accessToken,
    refreshToken,
  });
});

内容的提问来源于stack exchange,提问作者Harry 408

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:23:09