白嫖 Vercel 搭建 GitHub 加速代理
搞这个代理服务的初衷,其实很朴实:我需要一个能加速 git clone 的小工具,但又不想多掏一分钱去维护一台服务器。Vercel 的免费套餐在性能和流量上都非常慷慨,简直就是想白嫖资本家羊毛的最好选择。
我的需要部署一个轻量级服务,实现以下功能:
- 将所有请求转发到
https://github.com。 - 确保能处理大文件的
git clone和push,不能有限制。 - 访问主页
/时,返回一段简单明了的 Usage 文本。 - 设置
/robots.txt阻止搜索引擎收录。
为什么是 TypeScript + Edge?
一开始我根本没打算用 TypeScript。作为一个熟悉后端的人,第一反应是使用 Go 语言,因为 Go 标准库里的 ReverseProxy 作为反向代理简单高效。
我迅速用 Go 实现了第一版代码并部署到了 Vercel。在小流量测试中,一切正常。
但只要一尝试 git clone 稍微大一点的仓库,Vercel 云端 Log 会报 413 Payload Too Large。
Go 的失败,是 Serverless 架构的限制
这个问题不在于 Go 代码本身,而在于它运行的环境。
Vercel 的标准 Serverless Function(Go 就运行在这个层级上)底层基于 AWS Lambda 等技术,它在处理请求时,为了安全和稳定性,会缓冲 (Buffer) 整个请求体。当你要传输一个几 MB 甚至更大的 Git 数据包时,数据会在进入你的 Go 代码之前,就被Vercel 设置的 4.5MB 限制卡着1,然后返回 413 错误。
这让我意识到,要解决大文件传输,我必须绕过“缓冲”的限制,改用流式(Streaming)传输。
Edge Runtime:解决 413
Vercel 提供的 Edge Runtime2 基于 Web Standards (如 Fetch API 和 Web Streams),它的设计初衷就是为了处理 I/O 密集型任务,并且它默认支持请求和响应的流式透传。数据在 Edge 节点中像水流一样通过,不会被积压到内存中,Payload 大小限制自然也就不存在了。
既然决定转向 Edge,那么使用 TypeScript/JavaScript 就是最自然、最轻量且启动最快的选择。
Edge Runtime 的核心实现
核心代码逻辑集中在 api/index.ts 文件中,我们必须通过 Vercel 的配置,明确告诉它这个文件要跑在 Edge 环境下。
// 核心:告诉 Vercel 必须运行在 Edge Runtime
export const config = {
runtime: 'edge',
};
export default async function handler(req: Request) {
const url = new URL(req.url);
const path = url.pathname;
// 1. 优先级最高的路由处理: robots.txt
if (path === '/robots.txt') {
return new Response('User-agent: *\nDisallow: /', { status: 200 });
}
// 2. 主页路由:返回 Usage 说明
if (path === '/') {
const usageText = `USAGE
----------------
Clone a repo:
git clone https://github.therainisme.com/<user>/<repo>.git
...
`;
return new Response(usageText, {
headers: { 'content-type': 'text/plain; charset=utf-8' },
});
}
// 3. 核心代理转发逻辑
const targetUrl = new URL(req.url);
// 修正目标地址,确保转发到 github.com
targetUrl.protocol = 'https:';
targetUrl.hostname = 'github.com';
targetUrl.port = '';
const newReq = new Request(targetUrl.toString(), {
method: req.method,
headers: req.headers,
// 关键:req.body 就是 ReadableStream,直接传递实现流式透传
body: req.body,
redirect: 'manual',
});
// 必须设置正确的 Host Header,否则 GitHub 会拒绝请求
newReq.headers.set('Host', 'github.com');
// 移除 X-Forwarded-For 等头信息,保持请求的干净
newReq.headers.delete('X-Forwarded-For');
// 发起转发请求,并返回响应
const resp = await fetch(newReq);
// 返回 Response,确保流式数据被正确传递
return new Response(resp.body, {
status: resp.status,
statusText: resp.statusText,
headers: resp.headers,
});
}
仅代理 .git 和 .png 结尾的请求
非常重要! 如果我们对所有请求都进行转发,域名注册商可能会认为我们搭建一个钓鱼网站,从而对域名进行封禁。为了避免这种风险,我们需要加入路径过滤,只允许以 .git 或 .png 结尾的请求通过:
export default async function handler(req: Request) {
const url = new URL(req.url);
const path = url.pathname;
// robots.txt 和主页路由处理(同上,省略)...
// 重要:只代理 .git 和 .png 结尾的请求
if (!path.endsWith('.git') && !path.endsWith('.png')) {
return new Response('Forbidden: Only .git and .png requests are allowed', {
status: 403,
headers: { 'content-type': 'text/plain; charset=utf-8' },
});
}
// 后续的代理转发逻辑...
}
部署的两个小坎
代码虽然搞定了,但部署到 Vercel 时还是遇到了一些小问题,值得记录一下:
缺少
public目录:
由于这是一个纯 API 服务,项目根目录没有前端构建产物,Vercel 可能会报错Error: No Output Directory named "public" found。
解决方法: 在项目根目录随便创建一个空的public文件夹,里面放一个placeholder.html占位即可。根路由被静态文件劫持:
为了处理上面的问题,我在public里放了文件,但 Vercel 的默认路由机制是:静态文件优先级高于 API 路由。结果就是访问/时,显示的是public/index.html的内容,而不是我 TS 代码里精心编写的 Usage 文本。
解决方法: 不要将主页文件命名为index.html。我将public/index.html改名为public/placeholder.html,这样/路径就没有静态文件拦截,流量就顺利地走到了我的api/index.ts中。
最终总结
爱用!好用!
但好像只有上海爽用,根据群友反馈北京、深圳、西安似乎还比较卡。而且 Vercel 的 Edge Runtime 还有 300 秒的时间限制3,需额外注意。