最近拉 GitHub 上的 release 和 raw 文件,速度又回到了熟悉的几 KB/s。第三方聚合加速站(比如 github.akams.cn 那种测速导航)确实方便,但用别人的节点总有点不踏实:下载 URL 全程经过陌生人的 Worker,人家理论上能记录你访问了什么仓库、甚至改写内容,防不胜防。而且节点质量参差不齐,三天两头失联。
既然自己有 Cloudflare,干脆自建一个。
原理其实很简单
这类加速站的内核基本都是 hunshcn/gh-proxy 这个项目,逻辑一句话能讲完:把请求里的 GitHub 域名重写成代理域名,Worker 收到请求后转发给 GitHub,再把响应(顺便处理 302 跳转、CORS 头)流式吐回来。release 资产、raw 文件、archive 压缩包、git clone(smart http),理论上都走这一条链路。
这次我选了 Geekertao 的 CF-Workers-GitHub-Proxy——在原版基础上补了 GitHub API 转发和 release-assets 签名地址的内部跟随,单文件 7KB,还是熟悉的 service worker 格式。
部署:不用 wrangler,三条 curl 搞定
不想为一次部署在沙箱里装 npm 全家桶,直接走 CF 的 REST API。全程只需要一个 API Token(Account 级别的就够)。
第一步,拿 zone_id(域名已经在 CF 托管,前提条件):
curl -s "https://api.cloudflare.com/client/v4/zones?name=tmoc.qzz.io" \
-H "Authorization: Bearer $CF_TOKEN"
第二步,上传脚本。老式 service worker 格式,注意 multipart 的 metadata 里要写 body_part: "file"——写成 main_module 会被当成 ES Module 解析直接报错:
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT/workers/scripts/gh-proxy" \
-H "Authorization: Bearer $CF_TOKEN" \
-F 'metadata={"body_part":"file","compatibility_date":"2024-09-01"};type=application/json' \
-F 'file=@index.js'
第三步,绑定自定义域名。这一步会自动创建 DNS 记录并签发证书,不用去面板手动点,唯一的自定义动作就是想个好听点的子域名:
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT/workers/domains" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"zone_id":"<上一步拿到的>","hostname":"gh.tmoc.qzz.io","service":"gh-proxy","environment":"production"}'
脚本本体从 jsdelivr 拉的——毕竟 raw.githubusercontent.com 直连本来就不可靠,用镜像站去下载镜像站的源码,多少有点行为艺术。
验证
部署完逐项过了一遍:
- raw 文件:200,字节数与源文件一致
- archive zip:200,解包正常
- release 资产:拿 2.3MB 的 jq 二进制测的,经过 release-assets 签名跳转的内部跟随,完整拿到,
file识别为合法 ELF - git clone:
git ls-remote秒回 HEAD commit,clone 走通
一个预期内的坑:GitHub API 转发链路是通的,但匿名请求常年 403。GitHub 给匿名 API 的限额是 60 次/小时/IP,而 CF Worker 的出口 IP 是全球共享池,基本永远被别人打满。好在我的主场景是文件下载,不走匿名 API,问题不大——如果你重度依赖 API 转发,这个限制要有心理准备。
使用
前缀替换就完事:
# clone
git clone https://gh.tmoc.qzz.io/https://github.com/user/repo.git
# release 资产
https://gh.tmoc.qzz.io/https://github.com/user/repo/releases/download/v1.0/file.zip
# raw
https://gh.tmoc.qzz.io/https://raw.githubusercontent.com/user/repo/main/README.md
浏览器下载大文件前,建议把 Chrome/Edge 的多线程下载打开(chrome://flags/#enable-parallel-downloading,改成 Enabled 后重启),配合前缀替换并行拉,体感直接翻倍。
收尾
免费版 Worker 每天 10 万请求额度,个人使用绰绰有余。唯一要留意的是:自建域名是公开的,上线几分钟就会被扫描器收录,有人扫到会当免费公共节点白嫖。想限制的话,在脚本开头的 whiteList 数组里加上自己仓库的路径前缀再上传一版就行。
从动手到上线不到十分钟,成本为零。部署记录进了知识库,下次要换脚本版本或者改配置,重新 PUT 一次就完事——这大概是我最近性价比最高的一次折腾。
更新:加了访问验证
收尾里提到的 whiteList 我最后没用——路径白名单要维护列表,朋友想用还得先报仓库名。两天后我补了个更省事的门,把浏览器和命令行分开对待:
- 浏览器:第一次点链接会先进一个人机验证页(Cloudflare Turnstile),过一下自动回到原地址,之后 30 天免验证、不限速。验证状态用 HMAC 签名的 HttpOnly cookie 记住,伪造不了。
- 命令行(curl / git / wget):习惯完全不变,不用加任何参数,但走低额通道——每 IP 每分钟 30 次、每天 500 次,超了返回 429,等一分钟就恢复。
- 扫描器:没有 JS 环境,过不了验证页,永远卡在低额档,掀不起浪。
实现上有个值得记的坑:最初用 Cache API 做计数,并发一高 read-modify-write 就竞态,压测 33 并发一次都没拦住,限速形同虚设;换成 Durable Object 单实例串行计数才稳。另外在 Zone 层加了道 10 秒 300 请求的断路器兜底,防止验证通过后的多线程下载把额度瞬间打爆。
代价是命令行重度用户(比如 CI)要自己注意频次,或者干脆也过一次验证拿 cookie。对个人和小圈子使用来说,这个取舍我觉得值。