做后端开发这些年GitHub对我来说几乎是空气一样的存在。每天早上到工位的第一件事就是打开 github.com 看看有没有新 issue、新 release然后敲几条命令拉代码、看 CI 状态。但凡是经历过这种日常的开发者大概率都撞上过同一个场景——页面转圈标签页凉在那里两三分钟后弹出一句“无法访问此网站”。git clone 一个几 MB 的仓库速度经常跌到个位数 KB/s几十 MB 的 release 压缩包下载到一半直接中断。遇到这种情况很多人第一反应是搜“GitHub镜像站”要么用别人搭好的要么自己动手搭一个。前前后后我折腾了快两年从纯 Nginx 反代到 ghproxy 这类现成项目再到云函数转发踩了不少坑也积累了一些稳定运行的经验。这篇东西不打算写成那种泛泛的科普而是先把镜像站到底在干什么讲清楚再给你一套可以直接上手的 Nginx 方案最后把我踩过的坑全部摊开说。1. 为什么你需要一个GitHub镜像站1.1 让人崩溃的日常克隆、下载、访问到底慢在哪里先看问题本质。GitHub 本身是一个全球性的代码托管平台它的 CDN 节点分布确实很广但对于大陆地区用户来说从本机到 GitHub 数据中心的链路质量并不稳定。具体表现为三个阶段的问题第一DNS 解析结果不稳定同一个域名在不同网络环境下可能被解析到不同机房的 IP有些 IP 的线路绕路严重丢包率极高第二HTTPS 连接建立之后跨国链路的延迟通常在 200ms 以上遇到高峰时段甚至出现大量超时第三GitHub 下载 release 文件时会跳转到 objects.githubusercontent.com、codeload.github.com 这类资源域名而恰恰是这些域名在某些网络环境下表现最差。我见过不少团队代码本身不大但 CI 拉依赖、部署拉产物、同事 clone 仓库全都卡在网络上一天下来效率损失非常大。如果你所在团队的代码量不小、发布频率高把 GitHub 访问稳定性问题解决掉收益是非常直观的。1.2 镜像站到底是什么能做什么镜像站的本质是一个中转代理。它把 GitHub 上那些访问慢、不稳定的资源先拉到自己的服务器或者边缘节点上再由访问者从这个更快的节点下载。简单说就是“你访问镜像站镜像站帮你访问 GitHub然后把结果回传给你”。它适合解决四类典型场景git clone 仓库速度慢访问 github.com 网页超时下载 release 压缩包、二进制文件容易中断raw.githubusercontent.com 上的配置文件、脚本无法直接拉取。但镜像站并不是万能的。它主要解决“只读”场景比如拉代码、下文件、看网页。如果你要做 push、创建 Issue、管理仓库还是建议直接走 GitHub 官方通道或者使用官方客户端配合其他网络优化手段。后面会说清楚为什么写操作不适合走镜像。1.3 先别急着搭检查这三点再决定方案我见过很多一上来就买服务器、配置 Nginx 的朋友结果搭完之后发现访问速度还不如直接用 GitHub。原因往往出在准备工作没做够。动手之前建议先确认三件事。第一你的目标是什么。如果只是偶尔下载一两个开源项目的 release 包完全没必要自己搭镜像站用现成的 ghproxy 类公共服务就好省钱省心。如果你想在团队内部提供稳定的加速通道或者希望域名、流量都掌握在自己手里再考虑自建。第二你的服务器选在哪里。这一点极其重要。自建镜像站的逻辑是让服务器去访问 GitHub再让你的用户访问服务器。如果服务器放得离你很近但服务器到 GitHub 的链路很差最终速度依旧上不去。根据我的经验香港、日本、新加坡这些地区的服务器到 GitHub 和大陆的链路相对均衡是个人搭镜像站的优先选择。第三预算和运维能力。一台入门级 VPS、一个域名、几行 Nginx 配置就能跑起一个够用的镜像站。但如果你要服务上百人还要考虑带宽、磁盘缓存、流量控制这些事。先把定位想清楚后面不会返工。2. 镜像站的四种主流形态应该怎么选2.1 最通用Nginx反向代理Nginx 反向代理是自建镜像站最通用、最透明的做法。原理很简单你在自己的服务器上用 Nginx 监听某个域名或者路径遇到请求后转发到真正的 GitHub 域名并把响应原样返回给客户端。为什么这个方案最通用因为 Nginx 配置灵活你可以只代理 github.com 这一个域名也可以同时代理 raw.githubusercontent.com、codeload.github.com、objects.githubusercontent.com 等资源域名还可以配合 proxy_cache 做缓存配合 limit_req 做限流。改动起来很直观出了问题也容易排查。反代的缺点同样明显如果你只有一台服务器所有流量都要从这台上过带宽成本会随着用户量上升而且单点故障问题突出服务器一挂镜像站立刻不可用。不过对于个人使用、团队内部使用一台靠谱的 VPS 通常就够了。2.2 最省事开源现成方案ghproxy如果你不想自己写配置又希望有一个能直接跑的方案可以用 ghproxy 这类开源项目。这个项目基本上把 GitHub 反代的常见需求都封装好了支持标准 URL 拼接方式比如访问https://你的镜像地址/https://github.com/用户/仓库/archive/refs/heads/main.zip它会把后面的完整 GitHub 路径提取出来帮你访问并返回内容。这类现成方案的优点是上手快部署起来就是一个二进制或者一个 Docker 容器Gitee 上也有很多镜像仓库可以直接拉取。缺点是逻辑相对固定如果你想自定义 URL 结构、精细化控制缓存规则需要去改源码灵活性不如 Nginx 反代。我的建议是个人快速使用可以直接上 ghproxy团队长期维护还是用 Nginx 方案更可控。2.3 最飘逸云函数/边缘计算转发云函数和边缘计算是最近几年比较受欢迎的思路。你可以把转发逻辑写成一段函数部署到各种计算平台上向用户提供一个类似https://你的函数地址/github.com/用户/仓库的访问入口。这样做的好处是天然具备边缘节点用户从距离自己最近的节点获取数据缺点是免费额度通常有限大文件下载容易触发超时限制并且不同平台的调用方式和冷启动策略差异很大调优成本不低。这个方案适合什么场景我个人认为适合“临时体验”和“轻量访问”比如自己写点小工具时图个方便。真要给团队或者社区长期稳定提供服务边缘计算的成本不一定比一台 VPS 低尤其是在大流量下载场景下。2.4 最定向定时同步到本地/对象存储还有一种思路与上面完全不同不做实时反代而是通过 GitHub Actions 或者定时任务把某些仓库的代码、release 产物定时同步到自己的服务器、对象存储或内网 Git 服务上。团队内网部署一个 Gitea、GitLab然后开一个定时同步任务每天自动拉取上游仓库成员直接从内网访问。这个方案的优点是速度极快不受外部链路影响缺点是只能覆盖“你想同步的那些仓库”没法覆盖任意 GitHub 地址。它适合企业内部有明确依赖的仓库比如你依赖某个开源项目的指定版本定期同步下来做内部构建稳定性很高。它和“通用镜像”是两种思路可以结合起来用。2.5 一张表对比四种方案方案部署难度适用场景成本稳定性Nginx反向代理中等个人/团队通用加速服务器带宽费用较高开源现成方案ghproxy低快速使用服务器带宽费用中等云函数/边缘计算中等轻量、临时访问按调用量计费中高等定时同步到本地中等企业定向依赖内网/存储费用高如果你看完了还想往下走那我默认你选择了 Nginx 反代这条路线这也是我最熟悉、踩坑最深的方案。接下来直接进入实操。3. 完整实操Nginx搭建一个支持多域名的镜像站3.1 准备工作服务器、域名、解析先说服务器。建议选择香港、日本、新加坡等地区。系统优先选 Ubuntu 22.04 LTS 或 Debian 12这两个系统的 Nginx 包比较新apt 安装也省事。服务器规格不用太高个人使用 1 核 1G 就能跑带宽建议至少 5Mbps如果经常有人下载大文件带宽需要相应提高。域名部分强烈建议使用独立子域名而不是裸域名。比如你想做镜像站主站用hub.example.comraw 文件用raw.example.com下载资源用dl.example.com。独立子域名的好处是 Nginx 配置清晰后续证书管理、限流策略都可以按域名分开做出问题时也好排查。DNS 解析建议把记录指向服务器的公网 IPTTL 设短一点比如 300 秒方便后续调整服务器时快速生效。3.2 Nginx安装与基础优化安装很简单sudo apt update sudo apt install -y nginx nginx -v但我建议装完顺手把基础配置改一下。编辑/etc/nginx/nginx.conf主要调整几个点worker_processes auto让 CPU 自动分配worker_connections提到 4096避免连接数不够开启gzip on对文本类资源压缩传输量会小不少。还要记得设置server_tokens off;隐藏 Nginx 版本号减少被扫描的风险。worker_processes auto; events { worker_connections 4096; } http { server_tokens off; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; }这里有个经验gzip 配置尽量别对图片、压缩包这类本身已经是高压缩格式的资源开启否则白白消耗 CPU。后面讲缓存时还会提。3.3 核心配置四个关键域名分开反代GitHub 的资源域名其实不止一个我维护镜像站这么久总结下来最少要处理四个github.com网页、仓库主页、部分跳转raw.githubusercontent.comraw 文件、脚本、配置文件codeload.github.com仓库的 ZIP/TAR 压缩包下载objects.githubusercontent.comrelease 附件和部分动态资源的最终跳转地址。如果只代理github.com你会发现很多下载链接最终还是会暴露到其他域名速度依旧上不来。所以我建议按子域名分别做代理。以hub.example.com反代github.com为例在/etc/nginx/conf.d/github-mirror.conf里写server { listen 80; server_name hub.example.com; location / { proxy_pass https://github.com; proxy_set_header Host github.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 10s; proxy_read_timeout 60s; proxy_send_timeout 60s; } }重点解释一下这里的几个细节。proxy_pass https://github.com;采用的是默认反向代理方式Nginx 会以自身的 DNS 去解析 github.com建立到上游的 HTTPS 连接proxy_set_header Host github.com;是为了让 GitHub 收到请求时认为你在访问它本身避免返回异常X-Forwarded-*则是把客户端真实 IP 和协议透传给上游方便排查日志和做访问控制。把 raw、codeload、objects 分别对应到raw.example.com、dl.example.com、obj.example.com实际上就是复制上面的 server 块把server_name和proxy_pass、proxy_set_header Host换成对应域名即可。这里我不建议用一个域名加路径前缀去区分因为 GitHub 很多资源路径本身就是根路径开头的改写起来容易出 bug子域名方案是最干净的。3.4 HTTPS证书配置与自动续期镜像站不配 HTTPS 基本没法用。原因很简单现代 git 工具的默认策略以及浏览器安全机制会对 HTTPS 页面里的非 HTTPS 资源直接拦截如果你镜像站自己走 HTTP就会出现页面能打开但 clone 地址资源全部加载失败的情况。证书我用的是 Let’s Encrypt配合 certbot 插件自动签发和续期sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d hub.example.com -d raw.example.com -d dl.example.com -d obj.example.comcertbot 会自动修改 Nginx 配置并重载服务非常方便。这里有一个坑要提醒你如果你后面要加新子域名一定要记得重新跑一次上面的命令把新域名加进去否则新子域名的 HTTPS 访问会提示证书不匹配。自动续期配置完成后建议手动测试一次sudo certbot renew --dry-run确认输出没有报错。之后证书续期不用你管certbot 的 systemd timer 到时间会自动执行。3.5 缓存与限速别让服务器被打爆没有缓存的镜像站只是一个纯转发代理每个文件都从 GitHub 拉一遍带宽压力大速度也没有优势。Nginx 的proxy_cache可以帮你把热文件缓存到本地磁盘二次访问直接从本地返回速度会快很多。缓存配置要分两种情况处理。对于小文件比如 raw 脚本、网页资源可以大胆缓存对于 release 大文件比如几百 MB 的压缩包缓存会占满磁盘需要控制缓存大小和过期时间。下面是一份我实际在用的缓存配置思路proxy_cache_path /var/cache/nginx/gh levels1:2 keys_zonegh_cache:10m max_size20g inactive30d use_temp_pathoff; server { server_name raw.example.com; location / { proxy_pass https://raw.githubusercontent.com; proxy_set_header Host raw.githubusercontent.com; proxy_cache gh_cache; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 302 60m; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; } }解释几个关键参数levels1:2表示缓存目录采用两层子目录结构避免单个目录文件太多keys_zone10m表示缓存 key 的元数据内存大小10MB 可以存很多 keymax_size20g表示磁盘缓存最大 20GB超过以后 Nginx 按 LRU 算法清理proxy_cache_valid 200 302 60m表示 200 和 302 响应缓存 60 分钟。proxy_cache_use_stale是 Nginx 非常贴心的功能当上游出错时即使缓存过期了也会先返回旧版本避免用户直接看到 502。大文件下载场景下光有缓存还不够还需要限速和并发控制防止某个用户一口气把带宽占满。我的常用做法是配合limit_req和limit_ratelimit_req_zone $binary_remote_addr zonedl_limit:10m rate10r/s; limit_conn_zone $binary_remote_addr zonedl_conn:10m; server { server_name dl.example.com; location / { limit_req zonedl_limit burst20 nodelay; limit_conn dl_conn 5; limit_rate 10m; proxy_pass https://codeload.github.com; proxy_set_header Host codeload.github.com; } }rate10r/s是每秒最多处理 10 个请求超出后排队或者拒绝limit_conn 5是每个 IP 最多同时建立 5 个连接limit_rate 10m是单个连接的下行速度限制为 10MB/s。这几个数值只是参考实际根据你的服务器带宽调整。要是服务器带宽总共就 10Mbps限速反而是保护自己的一种方式。3.6 用起来设置git config与浏览器访问搭好以后你自己使用的时候可以把镜像站配到 git 里。以hub.example.com反代 github.com 为例最常用的方式是修改某个仓库的 remote 地址git remote set-url origin https://hub.example.com/用户名/仓库.git注意这里我建议把 remote 地址里的路径保持原样只是把域名换成你自己的镜像域名。如果需要临时加速下载某个仓库又不想改配置也可以直接 clone 镜像地址git clone https://hub.example.com/用户名/仓库.git浏览器访问则直接打开https://hub.example.com你会看到 GitHub 的界面只是域名变成了你自己的。但这里要提醒一句网页访问镜像站通常只能看公开仓库的代码和发行版登录、创建 issue、push 这类交互操作要么跳转会出问题要么因为凭证转发导致安全问题不建议通过镜像站做高强度操作。另外如果你想加速 SSH 方式的 git 操作可以参考一个非常实用的官方技巧通过 SSH over HTTPS 443 端口连接 GitHub。在~/.ssh/config里加一段Host github.com HostName ssh.github.com Port 443 User git这个方式不依赖镜像站但对日常 clone 和 push 的稳定性提升非常大而且完全合规属于 GitHub 官方提供的访问方式。4. 进阶玩法从“能访问”到“好用”4.1 给镜像站加一层CDN单台 VPS 的镜像站在访问量大起来之后瓶颈会非常明显。一个很实用的进阶方案是在镜像站前面再套一层 CDN。你可以在自己的域名接入 CDN 服务配置回源到 Nginx 服务器让用户从 CDN 边缘节点获取静态资源。这样 Nginx 实际承担的流量会大幅降低。这里有一个细节CDN 分两种情况理解。一种是用公共 CDN 的“缓存回源”模式边缘节点直接缓存镜像站上的文件。另一种是用云厂商的动态加速服务回源链路经过优化。前者配置简单适合释放大文件的下载缓存后者对 git 这类动态请求帮助更大但费用也更高。我个人建议先加缓存类 CDN把下载场景稳住动态请求量不大就别折腾了。加了 CDN 以后你的镜像站就变成了客户端访问 CDN 边缘节点CDN 回源到 NginxNginx 再回源到 GitHub。链路变长了但速度和容量都上来了。不过要提醒的是如果 CDN 回源链路本身不稳定这个方案的优势会打折扣选一个回源优的 CDN 服务商很关键。4.2 只读加速与推送隔离我在前面的章节反复提到“镜像站适合只读场景”这里要展开说清楚。镜像站在处理git clone、下载 release、读取 raw 文件时非常高效因为这本质是 HTTP GET 请求可以把响应缓存下来。但如果你通过镜像站去git push这里涉及写操作、认证凭证、跨域跳转任何一个环节出问题都可能导致推送失败更严重的是如果镜像站代码有漏洞你的 GitHub 凭证还有被截获的风险。所以我的建议是把镜像站定位成“只读加速器”push 操作永远走官方域名。团队内部还可以用同一个域名区分使用方式比如 CI 拉代码用镜像地址开发者 push 代码用官方地址。这样既享受了速度又不牺牲安全。如果你确实有团队内部集中推送的诉求不要试图让镜像站去代理 push而是应该在内部搭建 Git 服务用定时任务从 GitHub 同步代码再推送到内部仓库。这个方案前面表格里提到过虽然定向但是最稳妥的。4.3 防止被别人白嫖流量镜像站的流量是钱。如果你的域名是公开的很快会被网上的人扫描到然后当成公共镜像使用尤其是 release 大文件下载一晚上跑掉几百 GB 流量很常见。不想被白嫖至少要做两件基本的事。第一给下载子域名加上 Referer 校验。这个方案只对浏览器访问有效对 git 命令行没用但能挡住一部分恶意引用server { server_name dl.example.com; location / { if ($http_referer !~ ^https?://(hub\.example\.com|你的网站\.com)/) { return 403; } } }第二给整个镜像站加上访问控制。如果是团队内部使用最可靠的是auth_basicserver { server_name hub.example.com; auth_basic Restricted Access; auth_basic_user_file /etc/nginx/.htpasswd; }生成密码文件命令sudo apt install -y apache2-utils sudo htpasswd -c /etc/nginx/.htpasswd 用户名这种方式最简单直接保证只有知道账号密码的人才能使用。如果你连密码都不想维护可以把访问控制做成 IP 白名单但国内团队经常会有成员在家办公、IP 变动维护成本比较高密码认证其实是性价比最高的方案。5. 踩坑实录镜像站常见问题与排查速查5.1 灵魂拷问为什么一直502502 是我被问得最多的问题。先看下面几种最常见的原因。第一种Nginx 所在服务器无法访问 GitHub。这个可以通过命令行快速验证curl -I https://github.com curl -I https://raw.githubusercontent.com如果返回超时说明服务器到 GitHub 的链路本身就有问题这时候镜像站无论如何都起不来。解决办法是换服务器地区或者检查服务器自身的 DNS 配置是否正常。第二种Nginx 配置语法有问题。检查方法nginx -t tail -f /var/log/nginx/error.log配置文件有错时nginx -t会直接给出具体行数通常就是少了分号、括号没闭合这类低级错误。第三种upstream 的 SSL 连接失败。你服务器上的 CA 证书库过期或者 GitHub 的证书链在当前系统里无法验证Nginx 会在日志里留下SSL: certificate verify failed相关错误。解决办法是更新系统证书库sudo apt update sudo apt install -y ca-certificates5.2 下载中断、速度忽快忽慢下载 release 大文件时中断十有八九是超时配置或缓存配置的问题。第一检查proxy_read_timeout如果设置得偏短比如默认 60s遇到上游响应慢的大文件连接会被 Nginx 主动断开。第二检查磁盘剩余空间Nginx 缓存写满后会出现各种奇怪问题。第三检查限流配置如果你把limit_rate设置得过低比如 1m一个 200MB 的 release 包要下 200 秒体验肯定差。对大文件下载场景建议单独为下载子域名打开断点续传支持。Nginx 默认支持 HTTP Range 请求但如果配置了proxy_cache有部分场景会出现 Range 与缓存冲突需要确保proxy_cache_lock开启同时不要把大文件和小文件混在同一个缓存 zone 里。更稳妥的做法是raw 和页面走缓存大文件下载走独立的 server 块关闭缓存直接回源。5.3 canonical地址不对导致子模块失败这是我早期踩过的一个大坑。你正在 clone 的仓库里面如果有 submodule子模块地址写的是https://github.com/xxx/yyy.git。即便你用https://hub.example.com/xxx/yyy.git去 clonegit 在拉取子模块的时候还是会去请求github.com镜像站完全没起作用。解法有两种。一种是在~/.gitconfig里做 URL 重写[url https://hub.example.com/] insteadOf https://github.com/这个配置会把所有指向github.com的 HTTPS 请求自动替换成你的镜像站地址。适用于个人机器非常干净。另一种是在服务器端通过 Nginx 的sub_filter对 HTML 响应做替换把页面里出现的https://github.com/改写成https://hub.example.com/。这个方案又慢又容易漏我一般不建议你折腾。5.4 证书和git中的坑镜像站自己配了 HTTPS但 git clone 还是报 SSL certificate problem这种情况通常出在证书链不完整。证书链不完整导致部分旧版 Git 客户端无法验证到信任根会直接拒绝连接。使用 Let’s Encrypt 的证书时certbot 一般会把完整链写到配置里但如果你手动改过证书路径很容易只指到叶子证书。排查方式很简单用浏览器打开镜像站点地址栏的小锁图标查看证书链是否完整。如果只显示了域名证书而非完整链需要在 Nginx 配置里把ssl_certificate指向fullchain.pem而不是单独的cert.pem。还有一个 git 相关的小问题有些镜像站为了省事只配置了 HTTP 监听用户访问时会 301 跳转到 HTTPS但 git 工具对某些跳转处理不够聪明会出现无法访问的情况。所以干脆一步到位只配置 HTTPS 443 端口的 server 块别留 HTTP 入口。5.5 常见问题速查表问题现象可能原因快速处理页面打不开一直转圈服务器到GitHub链路差用curl -I https://github.com验证502 Bad GatewayNginx 上游证书或解析异常查 error.log更新 ca-certificatesclone 时子模块失败子模块地址仍是 github.com使用 git insteadOf URL 重写大文件下载中途断开超时过短或磁盘缓存写满调大 timeout独立下载 server 块网页能开但图片/脚本缺失只代理了主站没代理 raw/objects补上对应子域名反代访问缓慢单个IP占满带宽缺少限流和连接数控制配置 limit_req、limit_conn、limit_rategit 提示证书错误证书链不完整确认 ssl_certificate 指向 fullchain.pem我折腾 GitHub 镜像站折腾了两年最大的体会是不要追求把每个功能都搬到镜像上一个稳定、只读为主、够用的镜像站比一个三天两头挂掉的全能站有用得多。尤其是大文件下载宁可单独限速也不要让它拖垮主站的所有服务。另一个小技巧是对于团队里经常要用的 release 产物可以写个定时任务在凌晨流量低的时候先拉回到本地把热数据提前“捂热”白天用户下载时根本不需要回源速度体验会好很多。这些细节是网上那些通用教程不会教你的但恰恰是让镜像站真正“好用”的关键。