ZWCorsProxy:在浏览器播放任意跨域视频的本地 CORS 反向代理(mp4 + m3u8)
跨域视频在
<video>标签或 hls.js 里放不出来,因为源服务器不发送 CORS 头。ZWCorsProxy 用一招解决:在 URL 前加上本地前缀。代理在服务端取回内容、注入宽松 CORS 头再返回——mp4 透明转发(Range/206 完整保留),m3u8 播放列表自动重写分片 URL 让整条流都经代理。单文件二进制、零依赖、强制绑 127.0.0.1 永远不会变成开放代理。
下载(预编译,免安装):
免费 · 单文件自包含二进制 · 无需运行时 · 无需注册
为什么浏览器放不了跨域视频
你把一个视频嵌入网页——比如产品演示或托管在 CDN 上的 HLS 流——播放器就是加载不出来。控制台报一个红色的 CORS 错误:“已拦截跨源请求:请求的资源上不存在 ‘Access-Control-Allow-Origin’ 头。”
这是因为浏览器执行同源策略:yoursite.com 上的脚本不允许读取 cdn.example.com 的响应,除非该 CDN 通过 Access-Control-Allow-Origin 头明确允许。而大多数视频 CDN、防盗链源、媒体服务器根本不发这个头。
你改不了远端服务器的响应头。所以让视频能播放的唯一办法,是从一个不受 CORS 约束的地方去取——这正是服务端进程能做的事。ZWCorsProxy 就是这个进程,跑在你本机上。
ZWCorsProxy 做了什么
启动一次。然后对任意跨域视频,在 URL 前加上本地代理前缀:
http://127.0.0.1:8000/?url=https://cdn.example.com/video.mp4
代理做三件事:
- 在服务端取回该 URL(Go 的 HTTP 客户端不受浏览器 CORS 约束)。
- 注入宽松 CORS 头(
Access-Control-Allow-Origin: *等)后再返回。 - 对 m3u8/HLS,把播放列表里的每一个内部 URL(分片、密钥、变体)都重写成指向本代理,让整条流都经代理。
因为它跑在回环接口(127.0.0.1)上,没有额外的网络往返开销——速度只受上游连接带宽限制。
核心特性
| 特性 | 说明 |
|---|---|
| mp4 透明转发 | 二进制流原样透传,Range / 206 / Content-Range 完整保留——拖动进度条零延迟。 |
| m3u8 自动重写 | 播放列表的分片 URL、#EXT-X-KEY 的 URI、master 变体都重写成指向代理的 base64 路径,HLS 每一级请求都 CORS 干净。 |
| 宽松 CORS | 每个响应都带 Access-Control-Allow-Origin: *,且 Expose-Headers 含 Content-Range, Accept-Ranges, Content-Length——hls.js 需要的头都有。 |
| Range / 拖动支持 | 正确处理 Range 请求并返回 206 Partial Content(视频代理最常见的坑——返回 200 弄坏进度条——已避免)。 |
| 安全绑定 | 默认绑 127.0.0.1(回环)。可用 -host 绑定本机其它网卡 IP(如局域网 IP,供其它设备访问);0.0.0.0 被拒绝,防止变成开放代理。 |
| SSRF 防护 | 拒绝代理到私网段(10.x、172.16-31.x、192.168.x)、回环、链路本地、云元数据(169.254.169.254)、IPv6 ULA(fc00::/7)以及非 HTTP 协议。 |
| 可选 HTTPS | -https 用内嵌的 30 年自签证书跑 TLS(与 ZWServe 共用同一张证书,信任一次两个工具都能用)。 |
| 防盗链支持 | -referer 可伪造 Referer 头,对付校验来源的防盗链站。 |
| 零依赖 | 每个平台一个自包含 Go 二进制。无需运行时、无需 node_modules、免安装。 |
三端快速上手
Windows
双击 zwcorsproxy-win.exe,或在 PowerShell / cmd 里:
.\zwcorsproxy-win.exe
.\zwcorsproxy-win.exe -port 9000
macOS(Apple Silicon)
chmod +x zwcorsproxy-macos
./zwcorsproxy-macos
./zwcorsproxy-macos -port 9000
macOS 版针对 Apple Silicon(arm64)。Intel Mac 上可从源码编译:
GOOS=darwin GOARCH=amd64 go build zwcorsproxy.go。
Linux
chmod +x zwcorsproxy-linux
./zwcorsproxy-linux
./zwcorsproxy-linux -port 9000
怎么用
启动后,代理会打印自己的地址。在任意跨域视频 URL 前加上它,就能在任何原本用原始 URL 的地方使用:
http://127.0.0.1:8000/?url=https://example.com/video.mp4
http://127.0.0.1:8000/?url=https://cdn.example.com/master.m3u8
放进 HTML <video> 标签:
<video src="http://127.0.0.1:8000/?url=https://cdn.example.com/video.mp4" controls></video>
配合 hls.js:
hls.loadSource('http://127.0.0.1:8000/?url=https://cdn.example.com/master.m3u8');
如果目标 URL 本身带 query 参数(如
?token=abc),建议用encodeURIComponent整体编码,避免&被当成代理自身 query 串的一部分。
mp4 与 m3u8:各自怎么处理
| 内容 | 代理做什么 | 状态码 |
|---|---|---|
| mp4 / 二进制流 | 流式 io.Copy 透明转发。Range/Content-Range/Accept-Ranges 和状态码原样复制。 |
200 / 206(原样) |
| m3u8 播放列表 | 读全文,把内部每个 URL 重写成代理的 base64 路径,再写回。 | 200(body 已变) |
m3u8 重写是关键。如果代理只是原样返回播放列表文本,浏览器随后会直连原始 CDN 去取每个 .ts 分片——再次撞上同样的 CORS 错误。所以 ZWCorsProxy 把每一个引用(分片行、#EXT-X-KEY 的 URI=、#EXT-X-MEDIA 的 URI=、#EXT-X-MAP、master 变体等)都重写成 base64 路径:
http://127.0.0.1:8000/aHR0cHM6Ly9jZG4vc2VnbWVudC0wLnRz
浏览器请求这个路径时,代理把 base64 解码回原始 URL 再去取。整条 HLS 链路——master → 子 playlist → 分片——每一级都经代理,CORS 全程干净。
HTTPS(内嵌证书)
某些浏览器和播放器在 HTTPS 下表现更好(混合内容规则、安全上下文 API)。ZWCorsProxy 复用了与 ZWServe 相同的内嵌 30 年自签证书——绑定 127.0.0.1 + localhost,有效期 2026–2056。信任一次,两个工具都能跑 HTTPS。
# 导出证书到当前目录
./zwcorsproxy-win.exe -writecert
# → 产出 zwserver.crt / zwserver.key / zwserver.pem
# 以 HTTPS 启动
./zwcorsproxy-win.exe -https
把 zwserver.crt 装进系统信任库(Windows:certutil -addstore -f Root zwserver.crt;macOS:security add-trusted-cert …;Linux:拷到 CA 目录后跑 update-ca-certificates)。各平台安装脚本见 ZWServe 指南——证书完全相同。
与 ZWPlayer(及 M3U8Grab / ZWServe)搭配使用
ZWCorsProxy 是 zwplayer.com 流媒体工具家族的一员。它们覆盖三种不同场景:
| 工具 | 解决问题 | 什么时候用 |
|---|---|---|
| ZWCorsProxy | 跨域视频在线放不出来 | 想在浏览器里即时播放,不下载 |
| M3U8Grab | 想把跨域 m3u8 保存到本地离线 | 下载整条流(分片、密钥、字幕)本地播放 |
| ZWServe | 你的本地文件有 CORS 问题 | 给网页播放器提供带 CORS 的静态服务 |
搭配 ZWPlayer——一个功能完整的网页视频播放器——你可以把 ZWCorsProxy 的 URL 直接粘进播放器的源地址输入框,立刻播放任意跨域 mp4 或 m3u8,字幕、章节、播放列表功能都不受影响。
命令行参数
| 参数 | 默认值 | 说明 |
|---|---|---|
-port |
8000 |
监听端口。 |
-host |
127.0.0.1 |
绑定的本机 IP。必须是本机网卡实际存在的地址;0.0.0.0 被拒绝。用局域网 IP(如 192.168.1.50)可让其它设备访问。 |
-referer |
(空) | 伪造发给上游的 Referer(防盗链场景);空则用目标自身 host。 |
-https |
false |
用内嵌自签证书启用 HTTPS。 |
-cert |
(空) | 自定义 TLS 证书路径(PEM);需配 -key。 |
-key |
(空) | 自定义 TLS 私钥路径(PEM);需配 -cert。 |
-writecert |
false |
把内嵌证书导出到当前目录(zwserver.crt/.key/.pem)后退出。 |
-log |
on |
访问日志:on(默认)或 off。 |
-h |
— | 显示帮助。 |
常用配方
zwcorsproxy # 127.0.0.1:8000, HTTP
zwcorsproxy -port 9000
zwcorsproxy -host 192.168.1.50 # 绑局域网 IP(其它设备可访问)
zwcorsproxy -referer "https://example.com" # 绕防盗链
zwcorsproxy -https # HTTPS(需先信任内嵌证书)
zwcorsproxy -log off # 安静模式(播放器 seek 会刷屏日志)
常见问题
为什么默认绑 127.0.0.1?能绑局域网 IP 吗?
CORS 代理是个很有威力的东西——任何能访问它的人都能让它代替自己去请求任意 URL。所以默认绑 127.0.0.1(仅回环)。如果你想让局域网里的其它设备(手机、平板)也能用这个代理,用 -host <你的局域网IP> 启动(如 -host 192.168.1.50)。-host 的值必须是本机网卡实际存在的 IP,而 0.0.0.0(全网卡)被刻意拒绝——绑定全网卡会让你的机器变成开放代理,局域网里任何人都能借你的 IP 去爬虫或隐藏来源。
我代理了一个 m3u8,但分片还是失败,为什么?
确保你是通过代理请求 m3u8(?url=…/master.m3u8),而不是请求原始 URL。代理通过内容类型(Content-Type)或 #EXTM3U 前缀检测 m3u8,然后把分片 URL 重写成 base64 路径。如果你把播放列表文本复制出来直接请求分片,那些请求会绕过代理、撞上 CORS。
能用于非视频请求吗?
可以——它是一个带 CORS 头的通用 HTTP 反向代理。图片、JSON API、字体等都能用。但它是为视频优化的(Range 支持、m3u8 重写、长连接不超时)。非视频场景用通用工具可能更合适。
安全吗?SSRF 呢?
安全。除了绑回环,ZWCorsProxy 还会校验每一个目标:拒绝私网 IP 段(RFC1918)、回环、链路本地、云元数据地址(169.254.169.254)、IPv6 ULA(fc00::/7)以及非 HTTP 协议(file://、ftp:// 等)。转发前还会剥离浏览器的 Origin 和 Cookie 头,所以你的登录 cookie 不会泄给视频源。
会把我的 cookie 发给视频源吗?
不会。转发上游请求前会剥离 Cookie 和 Origin 头。代理用一个标准浏览器 User-Agent 和一个 Referer(你用 -referer 指定的,或缺省用目标自身 host)来标识自己。
总结
ZWCorsProxy 是让跨域视频在浏览器播放最简单的方法:在 URL 前加上本地前缀,搞定。mp4 流透明转发,Range/拖动完整支持;m3u8 播放列表自动重写,整条 HLS 链路 CORS 干净。它是单个自包含二进制,绑 127.0.0.1 带 SSRF 防护,并复用 ZWServe 的 TLS 证书可选 HTTPS。
搭配 ZWPlayer 试试——粘进一个代理 URL,就能播放任意跨域视频,字幕、章节、播放列表俱全。想离线保存,配合 M3U8Grab;想给自己的本地文件加 CORS,用 ZWServe。
— from zwplayer.com