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

问题修复

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

相关推荐
foundbug99919 分钟前
Modbus协议C语言实现(易于移植版本)
java·c语言·前端
Luna-player20 分钟前
在前端中list.map的用法
前端·数据结构·list
用户479492835691524 分钟前
面试官问 React Fiber,这一篇文章就够了
前端·javascript·react.js
小徐_233335 分钟前
Gemini 3做粒子交互特效很出圈?拿 TRAE SOLO 来实现一波!
前端·ai编程·trae
LYFlied36 分钟前
【一句话概述】Webpack、Vite、Rollup 核心区别
前端·webpack·node.js·rollup·vite·打包·一句话概述
reddingtons1 小时前
PS 参考图像:线稿上色太慢?AI 3秒“喂”出精细厚涂
前端·人工智能·游戏·ui·aigc·游戏策划·游戏美术
一水鉴天1 小时前
整体设计 定稿 之23+ dashboard.html 增加三层次动态记录体系仪表盘 之2 程序 (Q199 之2) (codebuddy)
开发语言·前端·javascript
刘发财1 小时前
前端一行代码生成数千页PDF,dompdf.js新增分页功能
前端·typescript·开源
_请输入用户名1 小时前
Vue 3 源码项目结构详解
前端·vue.js
少卿1 小时前
Next.js 国际化实现方案详解
前端·next.js