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

Set-Cookie已发送但Chrome应用面板看不到Cookie的生产环境问题

本地环境正常,生产环境中使用Express + Node开发的登录接口(POST /login)设置JWT Token,网络面板能看到Set-Cookie响应头(配置了httpOnly:true、secure: true、sameSite: "none"),但Chrome的Application > Cookies面板看不到该Cookie。

前端Axios调用代码:

const config = {
  withCredentials: true,
  credentials: "same-origin",
};

axios
  .post(
    `${endpoint}/login`,
    {
      email: loginData.email,
      password: loginData.password,
    },
    config
  )
  .then((response) => {
    console.log(response.status);
  });

后端Express代码:

const corsOptions = {
  origin: [
    "https://example.io", // 生产域名
    "https://www.example.io", // 带www的生产域名
    "http://localhost:3000",
  ],
  credentials: true,
};
app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "https://www.example.io");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Credentials", true);
  res.header("Access-Control-Expose-Headers", "Set-Cookie");
  next();
});
app.set("trust proxy", 1);
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(cookieParser());

app.post("/login", async (req, res) => {
  try {
    const {email, password} = req.body;
    const response = await pool.query(
      "SELECT id, (password = crypt($1, password)) AS verified FROM users WHERE email = $2",
      [password, email]
    );
    if (response.error) {
      console.log(response.error);
    }

    let {verified, id} = response.rows[0];
    if (verified) {
      const options = {
        expiresIn: "1h",
      };
      const token = jwt.sign(
        {user_id: id},
        process.env.SECRET_ACCESS_TOKEN,
        options
      );
      res.cookie("token", token, {
        maxAge: 86400000,
        secure: true,
        httpOnly: true,
        sameSite: "none",
      });
      res.json(response.rows[0]);
    } else {
      return res.status(401).json({message: "Invalid credentials"});
    }
  } catch (err) {
    console.error(err.message);
  }
});

解决方案

1. 修复CORS配置冲突

代码中同时使用了自定义CORS中间件和cors()包,导致Access-Control-Allow-Origin响应头固定为https://www.example.io,无法匹配主域名https://example.io的请求,且重复的头信息会引发浏览器拦截。

修改方案:移除自定义CORS中间件,通过corsOptions统一配置:

const corsOptions = {
  origin: [
    "https://example.io",
    "https://www.example.io",
    "http://localhost:3000",
  ],
  credentials: true,
  exposedHeaders: ["Set-Cookie"] // 显式暴露Set-Cookie头
};
app.set("trust proxy", 1);
// 仅保留cors()中间件,删除之前的自定义CORS处理逻辑
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(cookieParser());

2. 修正前端Axios的credentials配置

Axios中withCredentials: true已经包含跨域携带凭证的逻辑,重复设置credentials: "same-origin"会导致配置冲突,只需保留前者:

const config = {
  withCredentials: true
};

axios
  .post(
    `${endpoint}/login`,
    {
      email: loginData.email,
      password: loginData.password,
    },
    config
  )
  .then((response) => {
    console.log(response.status);
  });

3. 补充Cookie的Domain配置(按需)

如果需要主域名example.io和子域名www.example.io共享Cookie,需显式指定domain参数;如果前后端域名完全一致,可省略该配置:

res.cookie("token", token, {
  maxAge: 86400000,
  secure: true,
  httpOnly: true,
  sameSite: "none",
  domain: ".example.io" // 前置点适配主/子域名场景
});

4. 确保生产环境使用HTTPS

secure: true要求Cookie仅通过HTTPS传输,生产环境必须部署在HTTPS协议下,否则浏览器会直接拒绝存储该Cookie。

5. 检查Chrome隐私设置

Chrome默认拦截第三方Cookie,而sameSite: "none"属于跨域第三方Cookie范畴,需确认:

  • 关闭"阻止第三方Cookie"(设置 > 隐私和安全 > Cookie和其他网站数据)
  • 隐身模式下需单独设置允许第三方Cookie

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:55:26