Video 跨域播放、预加载缓存与防盗链:一条链路怎么讲清楚?
0. 面试速答(30 秒版 TL;DR)
- 视频跨域播放不是只配一个
Access-Control-Allow-Origin就结束了,真正要一起看的是:媒体资源 CORS、Range、CDN 缓存、播放器预加载策略、鉴权与防盗链。 <video>能不能播,和“能不能被 JS 正常读取状态/画到 canvas/做字幕和封面处理”不是同一件事;这时crossorigin、响应头、Cookie 策略都会影响结果。- 预加载不是越 aggressive 越好。
preload="auto"、metadata、首片预取、播放器预热,都要按场景选,不然很容易把流量和缓存打爆。 - 防盗链不要只靠
Referer。更稳的做法是:带过期时间的签名 URL / 鉴权 token + CDN 校验 + HTTPS,Referer只能当辅助手段。
1. 先看全链路:播放失败往往不是单点故障
视频页面里常见的几个症状:
- 页面能看到播放器,但一直转圈
- 某些浏览器能播,某些浏览器报跨域
- 拖动时重复回源,缓存命中很差
- 外站把你的资源地址一贴,照样能盗播
这些问题往往连在一起。
回答这类问题时,建议固定成 5 步:
- 资源是整文件还是分片流。
- 浏览器是不是跨域访问。
- 服务端是否正确支持
Range、缓存头、CORS。 - 前端预加载策略是不是过度。
- 链接是不是具备签名和过期机制。
2. 跨域播放到底在解决什么
2.1 先分清两个层次
第一层是“视频能不能播放”。
第二层是“前端脚本能不能安全地进一步操作视频资源”。
很多时候:
- 资源地址即使跨域,浏览器也可能直接播放。
- 但如果你要做这些事,就更依赖规范的跨域配置:
- 把视频帧画到
canvas - 读取更完整的媒体状态
- 做截图、封面抽帧、像素处理
- 某些播放器 SDK 的高级能力
- 把视频帧画到
2.2 前端常见写法
<video
controls
preload="metadata"
crossorigin="anonymous"
src="https://cdn.example.com/video/course-01.mp4"
></video>
这里的关键点:
crossorigin="anonymous"表示跨域请求不带凭证。- 如果服务端允许匿名跨域,就要返回匹配的 CORS 响应头。
- 如果你走的是 Cookie 鉴权,就不能再简单用匿名模式,要重新设计鉴权方式。
2.3 服务端最少要配什么
对于跨域媒体资源,至少要确认:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Expose-Headers,必要时暴露Content-Length、Content-RangeAccept-Ranges: bytes- 正确的
Content-Type
一个常见的 Nginx 例子:
location /video/ {
add_header Access-Control-Allow-Origin https://www.example.com always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
add_header Access-Control-Expose-Headers "Content-Length, Content-Range" always;
add_header Accept-Ranges bytes always;
}
如果是 HLS,还要继续检查:
m3u8清单是否允许跨域- 分片文件
ts/m4s是否同样允许跨域
常见坑是:清单放行了,分片没放行,最后表现成“播放器偶尔能播、偶尔报错”。
3. 为什么视频播放很依赖 Range
Range 的作用不是锦上添花,而是很多视频体验的基础能力。
没有 Range,会直接影响:
- 长视频拖动
- 断点续播
- 浏览器按需拉取
- CDN 对部分内容的缓存策略
正常情况下,播放器或浏览器会发这种请求:
Range: bytes=1048576-
服务端应该返回:
206 Partial ContentContent-RangeAccept-Ranges: bytes
如果这个链路没打通,典型现象就是:
- 拖动以后重新从头下
- 首帧慢
- 大文件回源压力很高
4. 预加载怎么选,不要只会写 preload="auto"
4.1 preload 的真实含义
preload 是浏览器的提示,不是强制命令。
常见取值:
none:尽量不预拉metadata:只拿元信息auto:允许浏览器积极加载
但实际加载多少,还受这些因素影响:
- 浏览器策略
- 网络环境
- 是否自动播放
- 是否在可视区
- 是否是移动端省流模式
4.2 场景化选择
| 场景 | 更常见的选择 | 原因 |
|---|---|---|
| 长视频详情页 | metadata | 先拿时长、封面、基础信息,避免一进页就拉大量流量 |
| 短视频信息流下一条预热 | 首片预取或业务层预热 | 比单纯 auto 更可控 |
| 直播间 | 进入房间后立即拉流 | 延迟优先,通常不是靠原生 preload 控制 |
| 后台管理播放 | none 或 metadata | 使用频率低,节省带宽 |
4.3 一个更务实的预加载原则
- 不要让页面首屏同时对多个视频都
auto。 - 不要把“预加载”理解成“把整个视频缓存到本地”。
- 预加载通常只解决首帧,不解决所有卡顿问题。
如果是信息流,常用思路反而是:
- 当前视频正常播放
- 只对下一条做轻量预热
- 离开可视区立即暂停并释放资源
5. 缓存到底缓存在什么地方
视频缓存至少有 3 层:
- 浏览器缓存:受
Cache-Control、资源类型、浏览器策略影响。 - CDN 缓存:核心看 URL、查询参数、分片粒度、回源规则。
- 播放器缓冲区:这是运行时 buffer,不等于持久缓存。
很多人把“buffer 很长”误以为“缓存已经很好”。这是错的。
5.1 前端更该关心的缓存头
Cache-ControlETagLast-ModifiedVary
但视频链路里,最关键的往往不是协商缓存本身,而是:
- 分片 URL 是否稳定
- 鉴权参数是否导致缓存穿透
- CDN 是否把签名参数纳入缓存键
5.2 一个典型误区
如果你给每个视频请求都挂上强随机参数,例如:
https://cdn.example.com/video/lesson.mp4?t=1712749200&nonce=abc123
那 CDN 很可能完全失去缓存价值,因为每个 URL 都变了。
更合理的做法通常是:
- 使用短期有效、可复用的签名 URL
- 控制签名参数结构,避免无意义抖动
- 对 HLS 清单和分片分别制定缓存策略
6. 防盗链怎么做才不脆弱
6.1 只靠 Referer 为什么不够
Referer 防盗链的优点是简单,但问题也明显:
- 某些浏览器或隐私策略会裁剪
Referer - App、内嵌 WebView、代理环境可能不稳定
- 被拿到真实地址后,单纯
Referer很容易被绕过
所以它更适合做:
- 粗粒度拦截
- 异常流量辅助识别
不适合做唯一鉴权手段。
6.2 更稳的方案
更推荐的组合是:
- URL 带签名
- 签名带过期时间
- 服务端或 CDN 校验签名
- 结合 HTTPS,防止链路中间人轻易截获
例如:
https://cdn.example.com/video/course-01.m3u8?expires=1712749800&sign=abcdef123456
校验要点通常包括:
- 路径
- 过期时间
- 用户或设备维度
- 防重放字段
6.3 HLS 场景下的防盗链要更细
HLS 不止一个 URL:
- 一个
m3u8 - 多个
ts或m4s - 可能还有密钥文件
所以防盗链不能只保护主播放地址,还要覆盖子资源,不然就会出现:
- 清单需要登录
- 分片却能被直接扫走
7. 一套常见的落地配置思路
7.1 点播 MP4
- 资源放 CDN
- 支持
Range Cache-Control允许 CDN 缓存- 使用带过期时间的签名 URL
- 需要前端抽帧时再加
crossorigin
7.2 HLS 点播
m3u8与分片都配 CORS- 清单可短缓存,分片可长缓存
- 签名要覆盖清单和分片
- 非 Safari 走
hls.js时要联调分片跨域
7.3 短视频信息流
- 当前视频正常播放
- 下一条轻量预热
- 离屏后暂停并释放
- 避免多个视频同时 aggressive preload
8. 常见追问
8.1 为什么视频地址浏览器里能直接打开,但页面里播放器报跨域?
因为“浏览器地址栏直接访问资源”和“页面脚本上下文里跨域访问媒体资源”不是同一个安全模型。尤其当你要结合 crossorigin、canvas、播放器库时,跨域规则会更严格。
8.2 为什么明明配了 CORS,拖动还是卡?
因为拖动更多依赖 Range、关键帧布局、分片长度、CDN 命中,不是只依赖 CORS。
8.3 为什么做了签名 URL,缓存反而变差?
常见原因是签名参数设计不稳定,导致同一个资源不断生成不同 URL,CDN 无法复用缓存。
9. 易错点
- 把“能播放”和“能跨域安全处理媒体”混为一谈。
- 只给主文件配 CORS,忘了 HLS 分片和密钥文件。
- 只配 CORS,不配
Range。 - 把
preload="auto"当成统一最优解。 - 只靠
Referer做防盗链。
10. 速记版
- 跨域播放要一起看:CORS +
crossorigin+Range+ 子资源。 - 预加载要按场景选,重点是首帧收益和流量成本平衡。
- 缓存要区分:浏览器缓存、CDN 缓存、播放器 buffer。
- 防盗链优先:签名 URL / token 鉴权,
Referer只做辅助。