在 Web 视频开发中,CORS(Cross-Origin Resource Sharing,跨域资源共享) 是最常见也最让人头疼的问题之一。当你满怀信心地将 M3U8 链接填入播放器,却只看到一片黑屏,Console 面板跳出红色的 CORS 报错——这种体验几乎每个前端开发者都经历过。本文将系统讲解 CORS 的原理,以及视频场景下的完整解决方案。
目录
浏览器为什么阻止跨域请求?
浏览器有一个核心安全机制叫同源策略(Same-Origin Policy):如果网页的协议、域名、端口号三者完全相同,则认为是"同源",可以互相访问资源;否则就是"跨域",浏览器会默认阻止。
这个策略是为了防止恶意网站读取你的银行页面、邮箱等敏感信息。但视频流服务器通常部署在独立域名或 CDN 上,必然产生跨域——这就需要 CORS 机制来明确授权。
CORS 请求的流程是怎样的?
CORS 请求分为两类:
- 简单请求:GET / HEAD / POST,且 Content-Type 为 text/plain、multipart/form-data 或 application/x-www-form-urlencoded。浏览器直接发送请求,并在请求头中添加
Origin字段 - 预检请求(Preflight):对于 PUT、DELETE 或自定义头部,浏览器会先发送一个
OPTIONS方法的预检请求,询问服务器是否允许
视频场景中的 M3U8 和 TS 请求都是 GET 请求,属于简单请求。但播放器库(如 hls.js)有时会添加自定义头部,触发预检。
🔴 常见 CORS 报错示例:
Access to XMLHttpRequest at 'https://cdn.example.com/video.m3u8' from origin 'https://player.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
视频场景中的特殊 CORS 问题
视频播放不是只请求一个 M3U8 文件就完事了,实际流程中涉及大量跨域请求:
- 播放器请求
.m3u8主播放列表 → 需要 CORS - 请求
.m3u8子播放列表(多码率时) → 需要 CORS - 请求
.ts视频片段(可能有数百个) → 每个都需要 CORS - 请求
.key解密密钥文件(加密流时) → 需要 CORS - 请求
.vtt字幕文件 → 需要 CORS
任何一个环节缺少 CORS 头,都会导致播放失败。只给 M3U8 文件加 CORS 是不够的,所有 TS 片段也必须配置。
Nginx 开启 CORS 配置
在 Nginx 配置文件的 server 或 location 块中添加:
location ~* \.(m3u8|ts|key|vtt)$ {
# CORS 头
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}
💡 注意 always 参数:Nginx 默认只在 200/201/204 响应中添加 add_header。对于 304 Not Modified 等状态码不会添加,导致缓存命中时 CORS 头丢失。always 参数确保所有状态码都返回 CORS 头。
Apache 开启 CORS 配置
<IfModule mod_headers.c>
<FilesMatch "\.(m3u8|ts|key|vtt)$">
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, OPTIONS"
Header set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Range"
</FilesMatch>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
</IfModule>
CDN CORS 配置
阿里云 CDN
在 CDN 控制台 → 域名管理 → HTTP 头 → 添加:
- 参数:
Access-Control-Allow-Origin - 取值:
*或指定域名如https://m3u8-player.cc
同时确保回源配置中开启"回源跟随 301/302"和"传递用户自定义头部"。
Cloudflare
Cloudflare 默认会缓存 CORS 头。在规则 → Transform Rules 中添加响应头转换,或者在源站返回头中设置,Cloudflare 会自动透传。
如何验证 CORS 是否配置正确
使用 curl 命令快速测试:
curl -I -H "Origin: https://m3u8-player.cc" \
"https://your-cdn.com/video/playlist.m3u8"
检查返回头中是否包含 access-control-allow-origin: * 或指定的域名。如果没有,说明 CORS 未正确配置。