理解 CORS:为什么有些视频链接无法播放,以及如何绕过

作者: OnlinePlayer Team
technicalcorssecuritystreamingdeveloper
理解 CORS:为什么有些视频链接无法播放,以及如何绕过

你找到了一个视频文件的直接 URL。也许是一个 m3u8 流,也许是某人服务器上的原始 MP4。你把它粘贴到网页播放器里,点击播放,然后……什么都没有。你打开浏览器控制台,看到类似这样的东西:

Access to XMLHttpRequest at 'https://video-server.com/stream.m3u8'
from origin 'https://my-player.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

欢迎来到 CORS 地狱。

如果你是开发者,你可能已经遇到过这个。如果你只是想看个视频,这简直让人抓狂。这篇指南会解释什么是 CORS,为什么它存在,以及——最重要的——如何绕过它。


CORS 到底是什么?

CORS 代表 跨源资源共享(Cross-Origin Resource Sharing)。它是一个浏览器安全功能,限制网页向与提供该页面的域不同的域发起请求。

"源"的概念

一个"源"(Origin)由以下定义:协议 + 域名 + 端口

这些是同源

  • https://example.com/page1
  • https://example.com/page2

这些是不同源

  • https://example.com vs http://example.com(不同协议)
  • https://example.com vs https://api.example.com(不同子域)
  • https://example.com vs https://example.com:8080(不同端口)

为什么它存在

没有 CORS 的话,任何网站都可以:

  1. 读取你在其他网站的 cookie
  2. 以你的身份向你的银行发起认证请求
  3. 从你已登录的 API 窃取敏感数据

CORS 是浏览器询问的方式:"嘿,video-server.com 的服务器,my-player.com 的页面访问你的内容可以吗?"

如果服务器没有明确说"可以"(通过 Access-Control-Allow-Origin 头),浏览器就会阻止请求。


为什么视频会受影响

这里变得有趣了。当涉及到 CORS 时,实际上有两种类型的请求:

1. 简单请求(通常被允许)

简单的 <video src="..."><img src="..."> 标签可以从任何源加载内容。这就是为什么你可以嵌入其他网站的图片。

<video src="https://other-site.com/video.mp4"></video>

能用。浏览器会加载视频,不管 CORS 头。

2. Fetch/XHR 请求(受 CORS 限制)

但如果你的视频播放器需要:

  • 通过 JavaScript 读取视频元数据
  • 解析 m3u8 播放列表(HLS 流媒体)
  • 添加自定义头(如认证令牌)
  • 访问视频数据进行处理

那它就必须使用 fetch()XMLHttpRequest这就是 CORS 生效的地方

// 没有正确的 CORS 头,这会失败
fetch("https://other-site.com/stream.m3u8")
  .then((res) => res.text())
  .then((playlist) => parseHLS(playlist));

M3U8 问题

HLS(HTTP 直播流)使用 .m3u8 播放列表文件,其中包含视频片段的 URL。要播放 HLS 流,播放器必须:

  1. 获取 m3u8 播放列表(受 CORS 限制)
  2. 解析它(JavaScript)
  3. 获取每个片段 URL(受 CORS 限制)

如果服务器不允许跨源请求,整个流就会失败——即使直接 MP4 播放正常工作。


如何判断是不是 CORS 问题

检查控制台

打开开发者工具(F12)→ 控制台。查找包含以下内容的错误:

  • "CORS policy"
  • "Access-Control-Allow-Origin"
  • "blocked by CORS"

检查网络标签

  1. 打开开发者工具 → 网络
  2. 找到失败的请求(会是红色的)
  3. 查看响应头
  4. 如果没有 Access-Control-Allow-Origin,这就是你的问题

快速测试

尝试在新的浏览器标签页中直接打开视频 URL。如果在那里能播放但在你的网页播放器中不能,几乎可以肯定是 CORS。


解决方案:服务器端修复

如果你控制视频服务器,修复很简单:添加正确的头。

对于 Apache (.htaccess):

<IfModule mod_headers.c>
  Header set Access-Control-Allow-Origin "*"
  Header set Access-Control-Allow-Methods "GET, OPTIONS"
  Header set Access-Control-Allow-Headers "Range"
</IfModule>

对于 Nginx:

location /videos/ {
  add_header 'Access-Control-Allow-Origin' '*' always;
  add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
  add_header 'Access-Control-Allow-Headers' 'Range' always;

  if ($request_method = 'OPTIONS') {
    return 204;
  }
}

对于 Cloudflare Workers:

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const response = await fetch(request);
  const newHeaders = new Headers(response.headers);
  newHeaders.set("Access-Control-Allow-Origin", "*");

  return new Response(response.body, {
    status: response.status,
    headers: newHeaders,
  });
}

对于 S3 / 云存储:

大多数云存储提供商都有 CORS 配置:

AWS S3:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedOrigins": ["*"],
    "ExposeHeaders": ["Content-Length", "Content-Range"]
  }
]

Google Cloud Storage:

[
  {
    "origin": ["*"],
    "method": ["GET", "HEAD"],
    "responseHeader": ["Content-Type", "Content-Length"],
    "maxAgeSeconds": 3600
  }
]

解决方案:当你不控制服务器时

这是棘手的部分。如果你不控制视频服务器,你的选择有限。

方案 1:CORS 代理

CORS 代理是一个服务器,代你获取资源并添加正确的头。

公共代理(有风险,经常限流):

  • https://cors-anywhere.herokuapp.com/(已停用)
  • https://api.allorigins.win/raw?url=

自托管代理(Cloudflare Worker 示例):

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const targetUrl = url.searchParams.get("url");

    if (!targetUrl) {
      return new Response("Missing url parameter", { status: 400 });
    }

    const response = await fetch(targetUrl, {
      headers: request.headers,
    });

    const newHeaders = new Headers(response.headers);
    newHeaders.set("Access-Control-Allow-Origin", "*");

    return new Response(response.body, {
      status: response.status,
      headers: newHeaders,
    });
  },
};

方案 2:浏览器扩展

像 "CORS Unblock" 或 "Allow CORS" 这样的扩展可以修改浏览器行为来忽略 CORS 限制。这能用,但:

  • 只对你有效(对你的用户无效)
  • 降低安全性
  • 有些网站会检测并阻止

方案 3:原生 Video 元素(用于 MP4)

如果是直接的 MP4/WebM URL(不是 HLS),原生 <video> 元素可能有效:

<video src="https://other-site.com/video.mp4" controls></video>

这可以绑过基本播放的 CORS(但你无法用 JavaScript 访问视频数据)。

方案 4:桌面应用

原生应用(Electron、Tauri)没有 CORS 限制,因为它们不在浏览器安全上下文中运行。


OnlinePlayer 的处理方式

OnlinePlayer 在可能的情况下使用浏览器原生视频元素处理视频播放,这有助于避免许多 CORS 问题。

对于直接 MP4/WebM URL:

  • 视频直接加载到 <video> 元素中
  • 基本播放不涉及 CORS

对于 HLS/DASH 流:

  • 这些确实需要服务器上的 CORS 头
  • 如果缺失,OnlinePlayer 会显示清晰的错误

对于云存储(Google Drive、Dropbox、OneDrive):

  • 使用 OAuth 认证
  • CORS 由云提供商的 API 处理

这就是为什么 OnlinePlayer 可以播放许多在其他网页播放器中失败的 URL——它在可能的情况下回退到最简单、最兼容的方法。


速查表

场景 解决方案
你拥有服务器 添加 Access-Control-Allow-Origin: *
AWS S3 / 云存储 在存储桶设置中配置 CORS
CDN(Cloudflare 等) 使用 Workers 或 Transform Rules
无服务器访问权限 + MP4 使用原生 <video> 元素
无服务器访问权限 + HLS 需要 CORS 代理
本地开发 使用浏览器扩展(临时)

总结

  1. CORS 是浏览器安全功能,不是 bug
  2. 服务器必须选择加入才能允许跨源访问
  3. 直接的 <video> 标签通常即使没有 CORS 头也能工作
  4. HLS/DASH 流总是需要 CORS,因为它们使用 JavaScript fetch
  5. 如果你不控制服务器,使用代理或使用原生元素的播放器

理解 CORS 会为你节省数小时的调试时间。当你碰壁时,至少你会准确知道是什么在阻止你。