M3U8 跨域(CORS)排障
浏览器播放 HLS 时,会分别请求 m3u8 清单和 ts/fmp4 分片。任意一个请求跨域未放行,都可能导致播放失败。很多人误以为“主清单能打开就没问题”,实际上播放器后续还会继续请求子清单、分片、密钥文件,任何一步被浏览器拦截都会表现为播放失败。CORS 问题的核心不是网络不可达,而是浏览器安全策略拒绝前端读取跨域响应。
一、典型现象与误区
最常见现象是控制台出现跨域错误,或 Network 里请求状态看起来是 200,但播放器仍报错。另一个高频误区是只给 m3u8 加跨域头,没有给 ts/fmp4 或 key 文件加同样策略,导致“首片可播、后续失败”。如果你启用了自定义请求头、Cookie 或 Authorization,还可能触发预检请求(OPTIONS),这时服务端必须正确响应预检,否则正式请求不会发送。
二、排查顺序(建议固定)
先确认清单和分片是否同源,不同源就必须统一跨域策略。再检查响应头是否完整返回,尤其是 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers。如果前端需要带凭证,不能使用通配符来源,必须返回明确域名并带 Access-Control-Allow-Credentials: true。第三步检查 CDN 是否覆盖了源站响应头,很多问题出在边缘节点模板没有同步更新。
三、Nginx 基础示例
location ~* \.(m3u8|ts)$ {
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "*" always;
}
四、生产环境建议
生产环境不建议长期使用全放开策略,最好按业务域名白名单返回来源。若涉及鉴权,清单、分片、密钥三类资源应使用一致的跨域与鉴权组合,避免规则分裂。部署完成后,务必在浏览器开发者工具中抽查主清单、子清单、分片、密钥四类请求的响应头。最后把 CORS 校验纳入上线前检查项,这样能在发布阶段就拦截大量"上线后才发现无法播放"的问题。
五、完整 Nginx 配置示例
以下是一个更完整的 Nginx 配置,包含了对预检请求的处理、对凭证请求的支持,以及针对不同文件类型的缓存策略:
server {
listen 80;
server_name stream.example.com;
root /var/www/stream;
# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Range, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400 always;
return 204;
}
location ~* \\.m3u8$ {
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Cache-Control' 'no-cache' always;
try_files $uri =404;
}
location ~* \\.ts$ {
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Cache-Control' 'public, max-age=600' always;
try_files $uri =404;
}
}
六、实际排查案例
某开发团队反馈,他们的直播流在 Chrome 中无法播放,但在 Safari 中正常。排查过程如下:
- 步骤1:查看 Chrome 控制台,发现大量 CORS 错误,提示 "Access-Control-Allow-Origin" 头缺失。
- 步骤2:检查 Nginx 配置,发现只配置了 .m3u8 文件的跨域头,没有配置 .ts 分片的跨域头。
- 步骤3:Safari 能播放是因为 Safari 原生支持 HLS,不通过 XHR 请求分片,不受 CORS 限制。
- 步骤4:在 Nginx 中添加 .ts 文件的跨域配置后,Chrome 播放恢复正常。
这个案例说明,跨域问题往往具有隐蔽性,不同浏览器的表现可能完全不同。排查时要结合浏览器开发者工具,逐一验证每个资源的响应头。
七、常见问题速查
- Q: 已经配置了 CORS,但播放器仍然报错?
A: 检查 CDN 是否覆盖了源站的响应头,很多 CDN 有独立的 CORS 配置。 - Q: 为什么本地开发环境正常,上线后出问题?
A: 本地开发通常是同源或禁用安全策略,线上环境跨域限制更严格。 - Q: 通配符 * 和具体域名有什么区别?
A: 使用 * 时不能携带凭证(Cookie/Authorization),带凭证时必须指定具体域名。