CORS 跨域完全指南:视频播放中的跨域问题与解决方案

在 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 文件就完事了,实际流程中涉及大量跨域请求:

  1. 播放器请求 .m3u8 主播放列表 → 需要 CORS
  2. 请求 .m3u8 子播放列表(多码率时) → 需要 CORS
  3. 请求 .ts 视频片段(可能有数百个) → 每个都需要 CORS
  4. 请求 .key 解密密钥文件(加密流时) → 需要 CORS
  5. 请求 .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 未正确配置。

在线测试您的 M3U8 视频流

CORS 配置完成后,使用我们的播放器验证是否能正常播放

前往 M3U8 在线播放器 →

相关文章: