一个部署在 Vercel 上的轻量 GitHub Raw 文件代理。访问者使用项目配置的令牌请求一个 GitHub 仓库文件,服务端向 raw.githubusercontent.com 获取文件,再以原始响应体返回。
它适合把 GitHub Raw 文件放到一个自有域名下,并统一增加访问令牌与缓存策略。项目刻意保持极简:没有前端、数据库、第三方依赖、构建步骤或测试框架。
请求链路如下:
客户端
│ GET /<owner>/<repo>/<branch>/<file-path>?nine-token=...
▼
Vercel rewrite
│ /api/github-raw.js?path=<原始路径>
▼
api/github-raw.js
│ 校验 nine-token
│ 可选地携带 GITHUB49TOKEN 请求 GitHub
▼
https://raw.githubusercontent.com/<owner>/<repo>/<branch>/<file-path>
│
└─ 非错误响应:状态码、允许的响应头和原始流式响应体返回给客户端
vercel.json 中的 rewrite 会把所有路径交给同一个函数:
/:path* → /api/github-raw.js?path=:path*
因此 URL 中的仓库路径不需要再写成 path 查询参数;path 是 Vercel 在内部注入的参数。访问代理根路径时,内部 path 为空。
- 将本仓库推送到你有权使用的 Git 仓库。
- 在 Vercel 中选择 Add New → Project。
- 导入该仓库。
- 保持默认的无构建部署方式;本项目没有需要执行的 build 命令。
- 在部署前配置下面的环境变量。
- 点击部署。
也可以使用 Vercel 的其他受支持导入方式,但必须确保项目根目录包含 vercel.json、api/github-raw.js 和 README.md 所描述的文件。
在 Vercel 项目的 Settings → Environment Variables 中配置:
| 变量名 | 是否必填 | 用途 |
|---|---|---|
NINE49TOKEN |
必填 | 代理入口令牌;请求中的 nine-token 必须与它完全一致 |
GITHUB49TOKEN |
可选 | 请求 GitHub Raw 时使用的 GitHub token,可提高受限流量场景下的可用性 |
环境变量修改后,需要按 Vercel 的规则重新部署或使新部署生效。令牌应只放在 Vercel 环境变量中,不要写入仓库文件、URL 示例的真实值、提交信息或公开日志。
如果没有配置 NINE49TOKEN,任何请求都会被拒绝并重定向;服务不会因此开放匿名访问。
https://<你的域名>/<owner>/<repo>/<branch>/<file-path>?nine-token=<NINE49TOKEN>
各部分含义:
<你的域名>:Vercel 分配的域名,或绑定到项目的自定义域名。<owner>:GitHub 用户名或组织名。<repo>:仓库名。<branch>:分支名、tag 或其他 GitHub Raw 支持的引用。<file-path>:仓库内文件路径;可以包含多级目录。nine-token:代理访问令牌,值必须与NINE49TOKEN完全一致。
下面的请求:
https://your-app.vercel.app/octocat/Hello-World/master/README?nine-token=YOUR_TOKEN
对应的上游地址是:
https://raw.githubusercontent.com/octocat/Hello-World/master/README
示例中的 YOUR_TOKEN 只是占位符,不是可用令牌。实际使用时应通过安全方式提供真实值。若文件路径包含空格、#、? 或其他 URL 特殊字符,应按 URL 规则进行编码。
查看响应头但不自动跟随错误重定向:
curl -i 'https://your-app.vercel.app/octocat/Hello-World/master/README?nine-token=YOUR_TOKEN'下载文件内容:
curl --fail --location \
'https://your-app.vercel.app/octocat/Hello-World/master/README?nine-token=YOUR_TOKEN' \
--output README--location 会跟随重定向;调试错误行为时建议去掉它,以便直接观察 302 和 Location 响应头。
当令牌有效、路径非空,且 GitHub Raw 返回的状态码小于 400 时:
-
透传上游状态码,例如
200或206。 -
响应体以流式方式转发,不包装成 JSON,也不改变文本、图片或二进制内容。
-
透传上游
Content-Type;如果上游没有该头部,则使用application/octet-stream。 -
按白名单透传
ETag、Last-Modified、Accept-Ranges和Content-Range。 -
设置固定缓存头:
Cache-Control: public, max-age=300, s-maxage=3600
以下情况都会记录访问摘要,然后返回 302 并跳转到固定地址:
| 情况 | 结果 |
|---|---|
nine-token 缺失或不正确 |
302 → https://www.baidu.com/ |
NINE49TOKEN 未配置 |
302 → https://www.baidu.com/ |
path 为空 |
302 → https://www.baidu.com/ |
| 请求 GitHub Raw 时发生网络错误 | 302 → https://www.baidu.com/ |
GitHub Raw 返回 400 或更高状态码 |
302 → https://www.baidu.com/ |
错误重定向的 Location 始终是固定百度地址,不包含原始路径、查询参数或任何令牌;错误响应使用 Cache-Control: no-store。
这意味着代理不会把上游 4xx/5xx 状态码或错误正文直接交给客户端,也不会继续提供原先可能存在的 400、403 或 502 错误正文。不会自动跟随重定向的客户端将看到 302;会自动跟随的客户端最终会访问固定地址。
代理固定设置:
- 浏览器缓存最多 300 秒(5 分钟)。
- Vercel Edge Cache 可缓存 3600 秒(1 小时)。
为支持协商缓存和范围请求,代理只向上游转发这些客户端请求头:
If-None-MatchIf-Modified-SinceRangeIf-Range
代理始终发送固定的 User-Agent。配置 GITHUB49TOKEN 时,还会向 GitHub Raw 发送 Authorization。该认证头不会返回给客户端,也不会写入运行日志。
响应头采用白名单策略,不会无条件复制 GitHub 的全部响应头。这样可以减少暴露上游实现细节的范围;如果要增加头部,必须同时评估缓存、隐私和兼容性影响。
每次进入函数时,服务会尽力向 Vercel Runtime Logs 的 Messages 写入一条脱敏访问摘要。摘要可能包含:
requestId- HTTP method
- 截断后的安全路径及路径长度
- 鉴权分类:
missing、not-configured、invalid或valid - 结果分类
- 客户端响应状态和上游状态(如有)
- 请求耗时
应用代码不会记录:
nine-token的值NINE49TOKEN或GITHUB49TOKEN的值Authorization- Cookie
- IP 地址
- 完整查询字符串
- 请求体或响应体
日志是“尽力记录”:Vercel 的保留策略、函数异常终止或平台不可用可能导致某条日志缺失或被截断。Vercel、浏览器和网络客户端仍可能显示请求域名、网络请求、响应状态和重定向链;应用日志不能彻底隐藏这些代理痕迹。
仓库没有 package.json,也没有依赖锁文件、构建系统、lint 配置、测试框架或 npm scripts。因此以下命令当前不存在且不应作为项目命令使用:
npm run buildnpm testnpm run lint- 单个测试文件命令
在仓库根目录执行:
node --check api/github-raw.jsnode --input-type=module -e "await import('./api/github-raw.js')"这两条命令只检查 JavaScript 是否能被 Node 解析/加载,不会模拟 Vercel 的 req/res,也不会发起 GitHub 请求。
git status --short
git diff -- api/github-raw.js README.md vercel.json CLAUDE.md
git diff --check将占位符替换为实际域名和令牌。调试重定向时不要加 -L:
curl -i 'https://<domain>/?nine-token=<NINE49TOKEN>'
curl -i 'https://<domain>/octocat/Hello-World/master/README'
curl -i 'https://<domain>/octocat/Hello-World/master/README?nine-token=<NINE49TOKEN>'可观察到的重点是:空路径和缺少令牌时出现固定 302,有效请求时状态码与上游非错误响应一致、响应体仍是原始文件,并带有固定 Cache-Control。有效请求检查依赖已部署的 Vercel 项目、正确环境变量、网络和 GitHub 上游可用性。
.
├── api/
│ └── github-raw.js # 唯一业务函数:鉴权、上游请求、响应与日志
├── vercel.json # 全路径 rewrite 配置
├── CLAUDE.md # 面向未来 Claude Code 实例的仓库指导
├── README.md # 面向使用者的部署与接口说明
├── License # 许可证文本
└── .gitignore # Git 忽略规则
- 这是固定用途的 GitHub Raw 代理,不是通用 HTTP 代理;不要未经明确需求增加任意目标主机、任意方法或任意响应头转发。
- 性能关键路径是响应体流式转发;不要改成
arrayBuffer()、Buffer或其他整包读入方式,否则大文件会增加内存压力。 path由 Vercel rewrite 注入。修改 rewrite、查询参数、环境变量、鉴权、状态码、重定向或缓存行为时,应同时更新相关代码和本文档。- 当前项目没有本地自动化行为测试。行为变更应在条件允许时通过已部署域名进行 HTTP 合约检查,并如实区分本地静态检查与部署后验证。
- GitHub Raw 的可访问性、限流、仓库权限和上游响应内容由 GitHub 决定;
GITHUB49TOKEN只影响代理请求上游时的认证,不会改变代理入口令牌规则。
许可证文本位于仓库根目录的 License 文件中。