前言
在前端开发中,跨域问题几乎是每个开发者都会遇到的"老朋友"。但你是否遇到过这样的怪事:同一个 CDN 域名,图片资源可以正常加载,视频资源却返回 403 Forbidden?更奇怪的是,直接在浏览器新标签页打开视频链接,视频又能正常播放。
本文将从一个真实案例出发,深入剖析浏览器对不同资源类型的跨域处理机制,彻底搞懂 Origin 和 Referer 请求头的区别,以及如何正确配置 CDN 解决此类问题。
背景:一个诡异的 403 问题
在 localhost 开发环境中,我遇到了一个奇怪的现象:
现象一:图片资源正常加载
请求 URL: https://cdn.xxxx.com/images/example.png
请求头:
Referer: http://localhost:5173/
(无 Origin 头)
响应: 200 OK
现象二:视频资源 403
请求 URL: https://cdn.xxxx.com/videos/example.mp4
请求头:
Origin: http://localhost:5173
Referer: http://localhost:5173/
响应: 403 Forbidden
现象三:直接访问视频链接正常
- 在浏览器新标签页直接打开
https://cdn.xxxx.com/videos/example.mp4,视频可以正常播放
这就引发了几个疑问:
- 为什么同一个 CDN,图片只有
Referer头,视频却同时有Origin和Referer头? - 为什么直接访问视频 URL 可以,页面中加载却 403?
- 这和 CDN 配置有什么关系?
核心原因分析
这个差异和 CDN 无关,是浏览器对不同媒体资源的跨域加载机制、CORS(跨域资源共享)规则的天然区别导致的——图片是「简易跨域资源」仅带Referer,视频是「需强CORS校验的资源」会同时带Origin+Referer,这也是本地image能访问CDN、video报403的核心原因(CDN/源站只校验了Referer却没配置CORS,拦截了带Origin的视频请求)。
浏览器对不同资源的跨域加载逻辑
浏览器为了安全和资源特性,对图片、字体、脚本这类「被动加载资源」和视频、音频、Fetch/AJAX这类「主动加载/可跨域读写的资源」制定了不同的跨域规则,直接导致请求头的差异,这是HTML5和CORS标准的规定,所有浏览器都遵循这个逻辑:
图片(image):仅触发「Referer溯源」,无CORS校验
图片属于**「无副作用的被动嵌入资源」,浏览器加载时不会触发CORS预请求(OPTIONS)**,仅会在请求头中携带Referer(告诉服务端资源是从哪个页面发起的),不会携带Origin头(Origin是CORS的核心标识,仅在强跨域校验时出现)。
- 适用规则:仅受Referer防盗链限制;
- 请求头特征:只有
Referer: http://localhost:5173/,无Origin。
视频(video/audio):触发「强CORS校验」,同时带 Origin + Referer
视频/音频属于**「可跨域读取数据的主动资源」(比如前端可通过video标签获取视频的宽高、时长等数据,甚至做跨域视频流处理),浏览器加载时会判定为「需要CORS授权的跨域请求」**,核心行为:
- 先触发CORS预请求(OPTIONS请求):先向CDN/源站发一个OPTIONS请求,携带
Origin: http://localhost:5173,询问服务端「是否允许这个源的跨域访问」; - 主请求同时带
Origin+Referer:即使预请求通过,后续的视频GET请求也会同时携带Origin(跨域源标识)和Referer(页面溯源); - 适用规则:既受Referer防盗链限制,又受CORS跨域配置限制(缺一不可)。
为什么视频会 403?
当前的CDN/源站只配置了「Referer防盗链白名单」(允许localhost的Referer),但完全没有配置「CORS跨域授权」:
- 图片请求:只有
Referer,匹配防盗链白名单,CDN直接放行,正常访问; - 视频请求:携带
Origin,CDN/源站因无CORS配置,会判定为「未授权的跨域请求」,直接返回403(部分CDN/源站对未授权的Origin会拦截,部分会在OPTIONS预请求阶段就返回403)。
Origin 和 Referer 的核心区别
很多人会混淆这两个头,这是浏览器发送的完全不同的两个标识,作用和触发场景天差地别,也是解决问题的关键:
| 特征 | Origin头 | Referer头 |
|---|---|---|
| 核心作用 | 标识跨域请求的源域名(CORS专用),用于服务端授权跨域 | 标识请求的发起页面URL,用于服务端溯源/防盗链 |
| 携带内容 | 仅包含「协议+域名+端口」,无路径,如http://localhost:5173 | 包含「协议+域名+端口+路径」,如http://localhost:5173/page/index.html |
| 触发场景 | 仅在CORS跨域请求中携带(视频/音频/Fetch/AJAX/POST) | 几乎所有非直接地址栏访问的请求都携带(图片/视频/脚本/链接跳转) |
| 是否可空 | 跨域请求中必须非空,否则浏览器会拦截请求 | 地址栏直接访问时为空,页面内加载时非空 |
实战演示:请求头对比
通过 Chrome DevTools 可以清晰看到差异:
图片请求 (img 标签)
<img src="https://cdn.xxxx.com/images/avatar.png" />
请求头:
GET /images/avatar.png HTTP/1.1
Host: cdn.xxxx.com
Referer: http://localhost:5173/
Accept: image/webp,image/apng,image/*,*/*;q=0.8
视频请求 (video 标签)
<video src="https://cdn.xxxx.com/videos/demo.mp4" controls></video>
预检请求 (OPTIONS):
OPTIONS /videos/demo.mp4 HTTP/1.1
Host: cdn.xxxx.com
Origin: http://localhost:5173
Access-Control-Request-Method: GET
主请求 (GET):
GET /videos/demo.mp4 HTTP/1.1
Host: cdn.xxxx.com
Origin: http://localhost:5173
Referer: http://localhost:5173/
解决方案
解决视频403的核心是:在CDN控制台为加速域名配置CORS规则,允许所有域名的跨域访问(CDN配置CORS后,源站无需再配,CDN会直接返回CORS授权头,效率更高)。
配置示例
CORS 响应头配置:
Access-Control-Allow-Origin: *
总结
通过这次深入分析,我们弄清了几个关键点:
-
浏览器对不同资源类型的跨域处理机制不同:
- 图片(img):简单跨域资源,仅发送
Referer,不触发 CORS 检查 - 视频(video):需要 CORS 授权的资源,会发送
Origin头并触发预检请求
- 图片(img):简单跨域资源,仅发送
-
Origin 和 Referer 的本质区别:
Origin:CORS 专用,标识跨域请求的源,格式为协议://域名:端口Referer:通用溯源头,标识请求来源页面,包含完整路径
-
403 问题的根源:
- CDN 仅配置了 Referer 防盗链,未配置 CORS
- 图片请求通过 Referer 验证即可访问
- 视频请求因缺少 CORS 响应头被浏览器或 CDN 拦截
-
解决方案:CDN 同时配置「Referer 防盗链」+ 「CORS 跨域授权」
扩展思考
这个问题揭示了前端开发中一个容易被忽视的细节:不同的 HTML 标签对同一资源的请求方式可能完全不同。类似的差异还存在于:
<script>vsfetch()请求 JS 文件<link>vs@import加载 CSS<iframe>vsfetch()加载 HTML
理解这些底层机制,不仅能帮我们解决当下的问题,更能让我们在架构设计时就避开这些坑。