记一次 CORS 深水坑:开启 withCredentials 后Response headers 只剩 content-type

背景与现象

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

排查过程:从"封装锅"到"CORS withCredentials"的反转

一开始以为是封装的请求库有问题,按"最可疑处优先"的思路做了这几步:

  1. 分析封装请求库是否有拦截器对 headers 字段做了限制过滤返回,排查下来无可疑点。

  2. 对比其他请求方式:axios、xhr 请求。

    javascript 复制代码
    import axios from 'axios'
    const res = await axios.post('xxxxxxx')
    • 结果正常!!!通过控制台打印可看到返回的 response headers 能看到更多头。
  3. 控制变量定位

    • 逐步排除了 URL、方法、请求体等差异,只有 "一个变量" 差异:withCredentials

    • 现象:

      • 打开 withCredentials: true → 只能读到 content-type 响应头。
      • 关掉 withCredentials: false → 可读到更多自定义头。
    • 基本锁定方向:与 "withCredentials" 传参相关,日常开发中使用请求库初始化随手就传入配置: withCredentials: true

根因:带凭证的跨域请求(withCredentials)触发更严格的 CORS 规则

  1. JS 可读响应头是受限的:默认只能读"简单响应头"(Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma)。
  1. 要读自定义响应头:后端必须设置 Access-Control-Expose-Headers,而且在带凭证请求下更严格:

    • 不能用 * 通配暴露响应头(会被忽略)

    • 必须显式列出要暴露的每个响应头(如 x-request-id, x-debugger-id),下图可看到 expose-headers 设置了通配符* ,所以无法显示。

  1. 带凭证的额外要求:

    • 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的DomainPath属性定义了其"作用域",浏览器仅会向匹配DomainPath的请求携带Cookie:

  • Domain:指定Cookie所属域名(如Domain=example.com,则a.example.comb.example.com均可携带,但需满足同源或跨域配置)。
  • Path:指定Cookie生效的路径(如Path=/api,则仅/api/data等路径的请求会携带,/page路径不携带)。

默认情况下 ,若Cookie未显式设置DomainPath,浏览器会自动将其作用域设为"当前页面的域名+当前路径"(如当前页面为https://example.com/admin/list,则Cookie默认Domain=example.comPath=/admin/),此时同源且路径匹配的请求会默认携带。

3. 跨域请求的"非默认"携带规则

非同源请求(跨域)下,浏览器默认不携带Cookie,需同时满足以下条件才会携带(此时不属于"默认"行为,需前端显式配置):

  • 前端配置 :通过XMLHttpRequestfetch设置withCredentials: true(如Axios中配置{ withCredentials: true })。
  • 后端配置 :响应头中返回Access-Control-Allow-Credentials: true,且Access-Control-Allow-Origin必须指定具体源(不能为*通配符)。
  • Cookie属性 :Cookie需设置SameSite=None(允许跨站携带)和Secure(仅HTTPS下生效),否则浏览器会因安全限制拒绝携带。

问题修复

这里不赘述了,相信看完上面 原因分析问题拓展,大家心里已有答案~

相关推荐
訾博ZiBo4 小时前
React组件复用导致的闪烁问题及通用解决方案
前端
临江仙4554 小时前
流式 Markdown 渲染在 AI 应用中的应用探秘:从原理到优雅实现
前端·vue.js
Hilaku4 小时前
为什么我开始减少逛技术社区,而是去读非技术的书?
前端·javascript·面试
m0_728033134 小时前
JavaWeb——(web.xml)中的(url-pattern)
xml·前端
猪哥帅过吴彦祖4 小时前
第 8 篇:更广阔的世界 - 加载 3D 模型
前端·javascript·webgl
七月十二4 小时前
[Js]使用highlight.js高亮vue代码
前端
Asort4 小时前
JavaScript设计模式(十二)——代理模式 (Proxy)
前端·javascript·设计模式
简小瑞5 小时前
VSCode源码解密:Event<T> - 类型安全的事件系统
前端·设计模式·visual studio code
寧笙(Lycode)5 小时前
OpenTelemetry 入门
前端