跨域请求携带 Cookie 的完整真相:一次说清所有误区
1. 前言:为什么又写这个话题?
基于之前我发的一篇文章Axios 如何跨域携带 Cookie?Axios 如何跨域携带 Cookie? 一、什么是跨域?核心是"同源策略" 1 - 掘金。 重新思考整理后发现,网上 90% 的文章只告诉你"前端加 withCredentials: true
,后端加 Access-Control-Allow-Credentials: true
就行"。但当你真去调试时,却发现:
- 第一次 GET 请求 根本没带 Cookie!
- 控制台报错:
Access-Control-Allow-Origin
不能用*
! - POST 居然发了 两次 请求?
本文用浏览器视角一次性讲透。
2. 前置知识:谁决定 Cookie 能不能跨域?
角色 | 职责 |
---|---|
浏览器 | 唯一裁判:决定是否发送/接受 Cookie |
前端 | 通过 withCredentials: true 告诉浏览器"我想带" |
后端 | 通过响应头告诉浏览器"我允许" |
⚠️ 只要浏览器不点头,即使后端返回 200,前端也拿不到数据。
3. 场景一:简单请求(GET/HEAD,无自定义头)
3.1 代码示例
csharp
// 前端:a.com
axios.get('https://b.com/api', { withCredentials: true });
3.2 真实流程
-
浏览器先裸发
- 带
Origin: https://a.com
- 不带 Cookie(无论后端是否允许)
- 无预检(preflight)
- 带
-
后端返回
yamlAccess-Control-Allow-Origin: https://a.com Access-Control-Allow-Credentials: true
-
浏览器检查通过后
- 把响应交给前端
- 缓存标记:下次同域请求可带 Cookie
-
第二次请求
- 浏览器自动带上 Cookie
3.3 结论
第几次请求 | 是否带 Cookie | 前端能否拿到数据 |
---|---|---|
第 1 次 | ❌ 不带 | ✅ 能(后端已正确配置) |
第 2 次及以后 | ✅ 带 | ✅ 能 |
所以第一次不会"失败",只是不带 Cookie。
4. 场景二:非简单请求(POST/PUT/DELETE 等)
4.1 触发条件
- 自定义头(如
Authorization
) - Content-Type 不是
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
4.2 代码示例
php
axios.post('https://b.com/api', { foo: 1 }, {
withCredentials: true,
headers: { 'Content-Type': 'application/json' }
});
4.3 真实流程(两次往返)
-
预检 OPTIONS
makefileOPTIONS /api HTTP/1.1 Origin: https://a.com Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type
-
不带 Cookie
-
后端需返回:
yamlAccess-Control-Allow-Origin: https://a.com Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: POST Access-Control-Allow-Headers: Content-Type
-
-
实际业务请求
- 此时才带 Cookie
- 后端再次返回相同 CORS 头
4.4 时间线图
markdown
浏览器 ──OPTIONS──> 后端
<─200 OK── (预检通过)
浏览器 ──POST+Cookie──> 后端
<─200 OK+数据──
5. 常见坑与排查清单
现象 | 原因 | 解决 |
---|---|---|
控制台报错 wildcard * |
后端返回 Access-Control-Allow-Origin: * |
改成具体域名 |
第一次 GET 没 Cookie | 浏览器策略:第一次永远不带 | 无需解决,第二次会带 |
POST 发两次 | 预检 OPTIONS | 正常行为 |
本地 http://localhost 调不通 | Cookie 需 Secure 属性 |
用 https 或改 Cookie 设置 |
6. 后端配置示例
Node.js(Express)
php
const cors = require('cors');
app.use(cors({
origin: 'https://a.com', // 不能是 *
credentials: true
}));
Nginx
ini
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
7. 一句话总结
跨域携带 Cookie 的钥匙始终握在浏览器手里:
第一次 GET 永远不带 Cookie,后端点头后,后续才带;
非简单请求需预检,真正业务请求第二次才带 Cookie。
8. 参考资料
- MDN - CORS
- Fetch Standard - CORS
- Chrome DevTools Network 面板
如果本文帮你理清了思路,点个 ⭐ 吧!