登录返回 200 但下一个请求立刻 401:Cookie Secure 标志在 HTTP 环境下的静默丢弃
给某个后台系统接入 Tauri 桌面壳,本地走 http://localhost:5891 调试。生产环境(HTTPS)登录一切正常,但本地启动后,登录接口返回 200,紧接着请求 /api/auth/me 就直接 401 了。重试、换账号、重启服务——全没用。
现象
Network 面板里,这两条请求背靠背出现:
POST /api/auth/login → 200 OK (服务端日志:session 已创建,Set-Cookie 已发送)
GET /api/auth/me → 401 (请求头里没有 Cookie 字段)
打开 DevTools 仔细看:
- Network → /login 响应头:
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Lax; Path=/✅ - Application → Cookies → localhost:5891:空的 ❌
服务端完全正常,Cookie 也确实被发出来了——但浏览器就是没存。
根因
Set-Cookie 里带了 Secure 标志。根据 RFC 6265 §4.1.2.5,这个标志要求用户代理只能在加密连接(HTTPS)下存储和发送该 Cookie。在 HTTP 连接下收到带 Secure 的 Cookie,浏览器会静默丢弃——没有任何 console 警告,没有网络错误,Set-Cookie 响应头照样可见,只是永远不会被写入本地存储。
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
^^^^^^
↑ HTTP 环境下,这一个词让整个 Cookie 被丢弃
Tauri 的 WebView(macOS 上是 WKWebKit,Windows/Linux 上是 WebKit2GTK 或 Chromium)同样严格遵守此规范——"我是桌面应用"不能绕过浏览器 Cookie 规则。
陷阱在于,很多框架/项目为了生产安全会默认开启 secure: true,但这个默认值在本地 HTTP 开发时同样生效:
// 有问题的写法:默认值是 'true',不传环境变量时也会启用 Secure
const COOKIE_OPTS = {
httpOnly: true,
secure: (process.env.COOKIE_SECURE || 'true') === 'true',
sameSite: 'lax' as const,
path: '/',
}开发者不设置 COOKIE_SECURE,值就是 'true',登录在本地永远失败——但错误信息完全指向 /me 接口鉴权,而不是 Cookie 本身。
Secure 标志导致的 Cookie 丢失没有任何 console 警告(与 SameSite 不同,SameSite 会有提示)。表面症状是"登录后立刻 401",引导开发者去查鉴权中间件、Session Store、JWT 校验——几乎不会往 Cookie 本身查。正确的第一步是:打开 DevTools → Application → Cookies,看 Cookie 是否真的被存下来了。
正解
让 secure 标志跟着环境走,本地 HTTP 开发时强制关闭:
// 正确写法:默认 false,生产由环境变量显式开启
const COOKIE_OPTS = {
httpOnly: true,
secure: process.env.COOKIE_SECURE === 'true', // 不传 = false
sameSite: 'lax' as const,
path: '/',
}在开发启动脚本里不设置(或显式传 false),生产部署时才传 true:
// package.json
{
"scripts": {
"dev": "tsx watch src/server/index.ts",
"app:dev:local": "COOKIE_SECURE=false npm --prefix desktop run tauri -- dev --config ..."
}
}# 生产部署
COOKIE_SECURE=true node dist/server.js如果是通过 concurrently / tauri dev 的 beforeDevCommand 启动子进程,环境变量会沿进程树向下传播,不需要每个子命令单独设置。
不要这样做:
- 全局关掉 HTTPS 重定向来"让本地过"——这破坏生产安全
- 用 Chrome 的
--unsafely-treat-insecure-origin-as-secure标志——不可移植,WebView 里也不起效 - 无条件改成
secure: false——生产必须保持true
secure: process.env.COOKIE_SECURE === 'true' 是最简单的修法。不传环境变量 = false,生产显式传 true。修完后验证:DevTools → Application → Cookies 里应能看到 session cookie,下一个请求的 Cookie 请求头里也应包含它。
60 秒快速定位
怀疑这个问题时,不需要看任何业务代码,先做三步:
- DevTools → Network → 点
/login响应 → 确认Set-Cookie响应头存在 - DevTools → Application (Chrome) / Storage (Firefox) → Cookies → 看 Cookie 是否被写入
- 找
Set-Cookie值里有没有Secure关键字
三步全中(Set-Cookie 有、Cookies 空、有 Secure)= 确认是这个坑,根因文件就是 Cookie 配置里的 secure 选项。
一句话外卖
secure: true的 Cookie 在 HTTP 下被浏览器静默丢弃——没有任何报错;正确姿势是用环境变量控制,本地 HTTP 默认false,生产 HTTPS 显式true。