记一次 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下生效),否则浏览器会因安全限制拒绝携带。

问题修复

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

相关推荐
Aotman_40 分钟前
JS 按照数组顺序对对象进行排序
开发语言·前端·javascript·vue.js·ui·ecmascript
Hi_kenyon8 小时前
VUE3套用组件库快速开发(以Element Plus为例)二
开发语言·前端·javascript·vue.js
起名时在学Aiifox8 小时前
Vue 3 响应式缓存策略:从页面状态追踪到智能数据管理
前端·vue.js·缓存
李剑一9 小时前
uni-app实现本地MQTT连接
前端·trae
EndingCoder9 小时前
Any、Unknown 和 Void:特殊类型的用法
前端·javascript·typescript
oden9 小时前
代码高亮、数学公式、流程图... Astro 博客进阶全指南
前端
GIS之路9 小时前
GDAL 实现空间分析
前端
JosieBook10 小时前
【Vue】09 Vue技术——JavaScript 数据代理的实现与应用
前端·javascript·vue.js
pusheng202510 小时前
算力时代的隐形防线:数据中心氢气安全挑战与技术突破
前端·安全