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

页面刷新后Cookie丢失问题求助(Express/React/JWT场景)

Express+React页面刷新后Cookie丢失问题分析与解决建议

问题描述

使用Express.js设置Cookie、React作为前端时,页面刷新或重载后Cookie会被删除,请求分析原因并提供解决建议。

相关代码

后端jwtToken.js

const sendToken = (user, statusCode, res) => {
    const token = user.getJWTToken();

    const options = {
        expires: new Date(Date.now() + process.env.COOKIE_EXPIRE * 24 * 60 * 60 * 1000),
        httpOnly: true,
        secure: true,
        domain: "shoppynexxa-backend.onrender.com",
    }

    res.cookie("token", token, options);

    res.status(statusCode).json({
        success: true,
        user,
        token,
    });
}

module.exports = sendToken;

后端登录控制器

//Login User
exports.loginUser = tryCatchWrapper(async (req, res, next) => {

    const { email, password } = req.body;

    //检查用户是否输入邮箱和密码
    if (!email || !password) {
        return (next(new ErrorHandler("Please Enter Email And Password", 400)))
    }

    const user = await User.findOne({ email }).select("+password");

    //检查用户是否存在
    if (!user) {
        return (next(new ErrorHandler("Invalid Email or Password", 401)));
    }

    //对比用户输入的密码和数据库中的密码
    const isPasswordMatched = await user.comparePassword(password);

    if (!isPasswordMatched) {
        return (next(new ErrorHandler("Invalid Email Or Password", 401)))
    }

    if(user.emailVerified) {
        sendToken(user, 200, res);
    } else {
        await sendVerificationEmail(user, res);
        return;
    }
});

前端api.js

// src/api.js
import axios from 'axios';

const api = axios.create({
    baseURL: 'https://shoppynexxa-backend.onrender.com', // 设置基础URL
    withCredentials: true,
    headers: {
        'Content-Type': 'application/json', // 示例请求头
    }
});

export default api;

前端useraction.js

//Login
export const login = (email, password) => async (dispatch) => {
    try {
        dispatch({ type: LOGIN_REQUEST });

        const config = { headers: { "Content-Type": "application/json" }, withCredentials: true}

        const { data } = await api.post(
            `/api/v1/login`,
            { email, password },
            config,
        );

        if (data.mailSent) {
            dispatch({ type: LOGIN_FAIL, emailVerification: !data.mailSent })
        } else {
            dispatch({
                type: LOGIN_SUCCESS,
                payload: data.user,
            })
        }
    } catch (error) {
        // console.log("error", error);
        dispatch({ type: LOGIN_FAIL, payload: error.response.data.message })
    }
}

原因分析

  1. Cookie Domain配置错误
    后端设置的domain: "shoppynexxa-backend.onrender.com"与前端域名属于完全不同的站点,浏览器会判定该Cookie不属于前端站点,不会将其持久化关联到前端域名下,刷新页面后自然无法读取到该Cookie。

  2. SameSite属性缺失
    当前Cookie配置未设置SameSite属性,现代浏览器默认采用Lax模式,在跨域场景下(前后端域名不同),该模式会限制Cookie的发送,导致刷新页面后Cookie无法被携带或保留。

  3. Secure属性的适配问题
    虽然线上环境是HTTPS,但如果secure属性强制设为true,在非HTTPS环境下浏览器会拒绝存储Cookie;同时跨域场景下SameSite设为none时必须搭配secure: true,当前配置缺少SameSite,也会导致Cookie存储异常。

解决建议

1. 修正Cookie配置

更新jwtToken.js中的Cookie选项,移除错误的domain配置,添加SameSite属性并根据环境动态设置secure:

const options = {
    expires: new Date(Date.now() + process.env.COOKIE_EXPIRE * 24 * 60 * 60 * 1000),
    httpOnly: true,
    secure: process.env.NODE_ENV === "production", // 生产环境启用,开发环境可关闭
    sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", // 跨域场景设为none
}

注意:当SameSite设为none时,secure必须设为true,否则浏览器会拒绝存储Cookie。

2. 配置正确的CORS规则

在Express后端添加CORS配置,允许前端域名并开启凭证支持:

const cors = require('cors');

app.use(cors({
    origin: "https://shoppynexxa.vercel.app", // 允许前端域名
    credentials: true, // 允许携带凭证(Cookie)
}));

如果需要支持多个环境(如开发、生产),可以根据环境变量动态设置origin。

3. 验证Cookie存储状态

登录成功后,打开浏览器开发者工具(F12),进入Application -> Cookies -> 前端域名,检查tokenCookie是否存在,确认其Expires、Secure、SameSite属性是否符合预期。如果Cookie未出现,说明配置仍有问题,需重新检查CORS和Cookie设置。

4. 前端请求确认

当前前端Axios已设置withCredentials: true,确保所有API请求(包括刷新页面后的初始化请求)都携带该配置,这样浏览器才会自动发送Cookie到后端。

内容的提问来源于stack exchange,提问作者Arjun Patel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:36:02