背景与现象
- 前端业务需要读取接口返回的自定义响应头(如 x-debug-id、x-trace-id 等),用来作为链路追踪。
- 浏览器 Network 面板能看到这些头,但在代码里读取接口返回 res.headers 时,只剩"简单响应头"(几乎只剩 content-type)。


排查过程:从"封装锅"到"CORS withCredentials"的反转
一开始以为是封装的请求库有问题,按"最可疑处优先"的思路做了这几步:
-
分析封装请求库是否有拦截器对 headers 字段做了限制过滤返回,排查下来无可疑点。
-
对比其他请求方式:axios、xhr 请求。
javascriptimport axios from 'axios' const res = await axios.post('xxxxxxx')
- 结果正常!!!通过控制台打印可看到返回的 response headers 能看到更多头。
-
控制变量定位
-
逐步排除了 URL、方法、请求体等差异,只有 "一个变量" 差异:withCredentials 。
-
现象:
- 打开 withCredentials: true → 只能读到 content-type 响应头。
- 关掉 withCredentials: false → 可读到更多自定义头。
-
基本锁定方向:与 "withCredentials" 传参相关,日常开发中使用请求库初始化随手就传入配置: withCredentials: true。
-
根因:带凭证的跨域请求(withCredentials)触发更严格的 CORS 规则
- JS 可读响应头是受限的:默认只能读"简单响应头"(Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma)。
-
要读自定义响应头:后端必须设置 Access-Control-Expose-Headers,而且在带凭证请求下更严格:
-
不能用 * 通配暴露响应头(会被忽略)
-
必须显式列出要暴露的每个响应头(如 x-request-id, x-debugger-id),下图可看到 expose-headers 设置了通配符* ,所以无法显示。
-

-
带凭证的额外要求:
-
Access-Control-Allow-Origin 必须是精确来源(不能是 *)
-
cors 必须返回 Access-Control-Allow-Credentials: true
-
问题拓展:什么情况下浏览器会默认携带cookie
1.请求与当前页面必须"同源"
浏览器遵循同源策略(Same-Origin Policy) ,仅当请求的目标URL与当前页面URL的协议、域名、端口三者完全一致时,才会默认携带Cookie。
-
同源定义 :
若当前页面URL为
https://example.com:8080/page
,则以下请求为同源:https://example.com:8080/api/data
(协议、域名、端口均一致)
以下请求为非同源(默认不携带Cookie):http://example.com:8080/api/data
(协议不同:http vs https)https://sub.example.com:8080/api/data
(域名不同:sub.example.com vs example.com)https://example.com:80/api/data
(端口不同:80 vs 8080)
2. Cookie自身属性未限制携带行为
即使请求同源,若Cookie设置了以下属性,浏览器仍可能默认不携带:
Cookie属性 | 作用 | 对"默认携带"的影响 |
---|---|---|
Secure |
仅允许HTTPS协议的请求携带Cookie。 | 若当前页面为HTTP协议,而Cookie设置了Secure ,则请求时默认不携带(因HTTP不满足HTTPS条件)。 |
SameSite=Strict |
仅允许"完全同源"的请求携带(禁止跨站请求携带,如从A网站跳转至B网站的请求)。 | 同源请求下仍默认携带;但跨站请求(即使目标URL与当前页面同源,如从外部链接直接打开同源页面)可能被限制(需结合浏览器实现)。 |
HttpOnly |
禁止JavaScript通过document.cookie 访问Cookie,但不影响请求携带。 |
不影响默认携带,仅限制前端脚本读取,请求时浏览器仍会自动附加。 |
2. 相关机制
浏览器默认携带Cookie的行为,本质是 "同源信任"与"安全隔离" 平衡的结果,核心机制包括:
1. 同源策略(Same-Origin Policy)
同源策略是浏览器的核心安全机制,目的是防止不同源的网站非法访问彼此的资源(包括Cookie)。对于同源请求,浏览器认为"可信",因此自动附加Cookie;对于非同源请求,默认"不信任",需额外配置才允许携带Cookie(见下文"跨域场景")。
2. Cookie的"作用域"与"路径"匹配
Cookie的Domain
和Path
属性定义了其"作用域",浏览器仅会向匹配Domain
和Path
的请求携带Cookie:
Domain
:指定Cookie所属域名(如Domain=example.com
,则a.example.com
和b.example.com
均可携带,但需满足同源或跨域配置)。Path
:指定Cookie生效的路径(如Path=/api
,则仅/api/data
等路径的请求会携带,/page
路径不携带)。
默认情况下 ,若Cookie未显式设置Domain
和Path
,浏览器会自动将其作用域设为"当前页面的域名+当前路径"(如当前页面为https://example.com/admin/list
,则Cookie默认Domain=example.com
、Path=/admin/
),此时同源且路径匹配的请求会默认携带。
3. 跨域请求的"非默认"携带规则
非同源请求(跨域)下,浏览器默认不携带Cookie,需同时满足以下条件才会携带(此时不属于"默认"行为,需前端显式配置):
- 前端配置 :通过
XMLHttpRequest
或fetch
设置withCredentials: true
(如Axios中配置{ withCredentials: true }
)。 - 后端配置 :响应头中返回
Access-Control-Allow-Credentials: true
,且Access-Control-Allow-Origin
必须指定具体源(不能为*
通配符)。 - Cookie属性 :Cookie需设置
SameSite=None
(允许跨站携带)和Secure
(仅HTTPS下生效),否则浏览器会因安全限制拒绝携带。
问题修复
这里不赘述了,相信看完上面 原因分析 和 问题拓展,大家心里已有答案~