使用 Caddy 部署 Github 镜像

目录

需要替换的字符串:

github.my-gh-proxy.com
assets.my-gh-proxy.com
avatars.my-gh-proxy.com
raw.my-gh-proxy.com
api.my-gh-proxy.com
codeload.my-gh-proxy.com
objects.my-gh-proxy.com
release-assets.my-gh-proxy.com
gist.my-gh-proxy.com
.my-gh-proxy.com # 最后替换这一个
# 基础代理头设置
(proxy_defaults) {
	# 伪装主机头,通过 upstream_hostport 自动获取反代目标的主机名
	header_up Host {upstream_hostport}

	# 传递真实客户端信息
	header_up X-Real-IP {remote}

	# 关键:禁用上游压缩,以便 replace 模块能修改响应体
	header_up Accept-Encoding identity
}

# 隐私与安全头
(security_headers) {
	header X-Robots-Tag "noindex, nofollow, noarchive"
	header X-Content-Type-Options "nosniff"
	# 移除 CSP 以防止域名不匹配导致的脚本拦截
	header -Content-Security-Policy
}

# 1. 主域名: github.my-gh-proxy.com
github.my-gh-proxy.com {
	# 代理端开启压缩,提升客户端加载速度
	encode zstd gzip

	import security_headers

	reverse_proxy https://github.com {
		import proxy_defaults

		# 重定向重写
		header_down Location https://github.com https://github.my-gh-proxy.com
		header_down Location https://objects.githubusercontent.com https://objects.my-gh-proxy.com
		header_down Location https://release-assets.githubusercontent.com https://release-assets.my-gh-proxy.com
		header_down Location https://raw.githubusercontent.com https://raw.my-gh-proxy.com

		# Cookie 域名重写 (正则匹配)
		header_down Set-Cookie "(.*)Domain=\.github\.com(.*)" "$1Domain=.my-gh-proxy.com$2"
	}

	# 内容替换规则
	replace {
		stream
		"https://github.com" "https://github.my-gh-proxy.com"
		"https://github.githubassets.com" "https://assets.my-gh-proxy.com"
		"https://avatars.githubusercontent.com" "https://avatars.my-gh-proxy.com"
		"https://raw.githubusercontent.com" "https://raw.my-gh-proxy.com"
		"https://api.github.com" "https://api.my-gh-proxy.com"
		"https://codeload.github.com" "https://codeload.my-gh-proxy.com"
		"https://objects.githubusercontent.com" "https://objects.my-gh-proxy.com"
		"https://release-assets.githubusercontent.com" "https://release-assets.my-gh-proxy.com"
		"https://gist.github.com" "https://gist.my-gh-proxy.com"
	}
}

# 2. 静态资源: assets.my-gh-proxy.com
assets.my-gh-proxy.com {
	encode zstd gzip
	import security_headers

	reverse_proxy https://github.githubassets.com {
		import proxy_defaults
		header_down Access-Control-Allow-Origin "*"
	}
}

# 3. 头像服务: avatars.my-gh-proxy.com
avatars.my-gh-proxy.com {
	encode zstd gzip
	import security_headers

	reverse_proxy https://avatars.githubusercontent.com {
		import proxy_defaults
		header_down Access-Control-Allow-Origin "*"
	}
}

# 4. 原始文件: raw.my-gh-proxy.com
raw.my-gh-proxy.com {
	encode zstd gzip
	import security_headers

	reverse_proxy https://raw.githubusercontent.com {
		import proxy_defaults
		header_down Access-Control-Allow-Origin "*"
	}

	replace {
		stream
		"https://github.com" "https://github.my-gh-proxy.com"
		"https://raw.githubusercontent.com" "https://raw.my-gh-proxy.com"
	}
}

# 5. API 服务: api.my-gh-proxy.com
api.my-gh-proxy.com {
	encode zstd gzip
	import security_headers

	reverse_proxy https://api.github.com {
		import proxy_defaults
		# 修复分页链接
		header_down Link "https://api.github.com" "https://api.my-gh-proxy.com"
		header_down Location https://api.github.com https://api.my-gh-proxy.com
	}

	# 替换 JSON 响应中的 URL
	replace {
		stream
		"https://api.github.com" "https://api.my-gh-proxy.com"
		"https://github.com" "https://github.my-gh-proxy.com"
		"https://raw.githubusercontent.com" "https://raw.my-gh-proxy.com"
	}
}

# 6. 代码下载 (Zip): codeload.my-gh-proxy.com
codeload.my-gh-proxy.com {
	encode zstd gzip
	import security_headers

	reverse_proxy https://codeload.github.com {
		import proxy_defaults
	}
}

# 7. 对象存储 (LFS/Releases): objects.my-gh-proxy.com
objects.my-gh-proxy.com {
	# 禁用压缩以提高传输大文件的效率(避免 CPU 浪费)
	# import security_headers # 对象存储通常不需要过多的安全头干扰

	reverse_proxy https://objects.githubusercontent.com {
		import proxy_defaults
		# 确保不修改 Authorization 头,透传 S3 签名
	}
}

# 7.5. Release 资产(2026 年起 GitHub 迁移至独立域): release-assets.my-gh-proxy.com
# 不配置此域名时,release 下载的 302 重定向会指向 release-assets.githubusercontent.com,
# 客户端将直连 GitHub 官方域,绕过镜像(且官方域在受限网络下不可达)。
release-assets.my-gh-proxy.com {
	# 与 objects 同理,禁用压缩,大文件传输避免 CPU 浪费
	# import security_headers

	reverse_proxy https://release-assets.githubusercontent.com {
		import proxy_defaults
	}
}

# 8. Gist 服务: gist.my-gh-proxy.com
gist.my-gh-proxy.com {
	encode zstd gzip
	import security_headers

	reverse_proxy https://gist.github.com {
		import proxy_defaults
		header_down Location https://gist.github.com https://gist.my-gh-proxy.com
		header_down Set-Cookie "(.*)Domain=\.github\.com(.*)" "$1Domain=.my-gh-proxy.com$2"
	}

	replace {
		stream
		"https://gist.github.com" "https://gist.my-gh-proxy.com"
		"https://github.com" "https://github.my-gh-proxy.com"
		"https://github.githubassets.com" "https://assets.my-gh-proxy.com"
		"https://avatars.githubusercontent.com" "https://avatars.my-gh-proxy.com"
	}
}

进阶:basic auth 与同源化

镜像站通常不想被公开使用,最省事的保护是给每个站点块加 basic auth:

github.my-gh-proxy.com {
	basic_auth {
		user $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
	}
	# ...其余配置不变
}

密码用 caddy hash-password --plaintext '你的密码' 生成。

但多子域 + basic auth 有一个坑:浏览器按域名分别保存凭据。你在 github.my-gh-proxy.com 输入过密码,浏览器不会把这份凭据自动发给 assets.my-gh-proxy.com 等子域——于是首页能打开,JS/CSS/头像等跨域资源全部 401,页面只剩骨架。每个子域都要手动输一次密码,换设备或清缓存后还得重来。

解法是同源化:只保留主域名,其余上游全部用 handle_path 按路径分流,凭据只需一份:

github.my-gh-proxy.com {
	basic_auth {
		user $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
	}

	# 静态资源: github.githubassets.com
	handle_path /ghassets/* {
		encode zstd gzip
		reverse_proxy https://github.githubassets.com {
			import proxy_defaults
			header_down Access-Control-Allow-Origin "*"
		}
	}

	# 头像: avatars.githubusercontent.com
	handle_path /ghavatars/* {
		encode zstd gzip
		reverse_proxy https://avatars.githubusercontent.com {
			import proxy_defaults
			header_down Access-Control-Allow-Origin "*"
		}
	}

	# 原始文件: raw.githubusercontent.com
	handle_path /ghraw/* {
		encode zstd gzip
		reverse_proxy https://raw.githubusercontent.com {
			import proxy_defaults
			header_down Access-Control-Allow-Origin "*"
		}
	}

	# API: api.github.com
	handle_path /ghapi/* {
		encode zstd gzip
		reverse_proxy https://api.github.com {
			import proxy_defaults
			header_down Link "https://api.github.com" "https://github.my-gh-proxy.com/ghapi"
			header_down Location https://api.github.com https://github.my-gh-proxy.com/ghapi
		}
	}

	# 代码下载 (Zip): codeload.github.com
	handle_path /ghcodeload/* {
		encode zstd gzip
		reverse_proxy https://codeload.github.com {
			import proxy_defaults
		}
	}

	# 对象存储 (LFS): objects.githubusercontent.com
	handle_path /ghobjects/* {
		reverse_proxy https://objects.githubusercontent.com {
			import proxy_defaults
		}
	}

	# Release 资产: release-assets.githubusercontent.com
	handle_path /ghreleaseassets/* {
		reverse_proxy https://release-assets.githubusercontent.com {
			import proxy_defaults
		}
	}

	# Gist: gist.github.com
	handle_path /ghgist/* {
		encode zstd gzip
		reverse_proxy https://gist.github.com {
			import proxy_defaults
			header_down Location https://gist.github.com https://github.my-gh-proxy.com/ghgist
			header_down Set-Cookie "(.*)Domain=\.github\.com(.*)" "$1Domain=.my-gh-proxy.com$2"
		}
	}

	# 主站: github.com(兜底,匹配所有其他路径)
	handle {
		encode zstd gzip
		import security_headers

		reverse_proxy https://github.com {
			import proxy_defaults

			header_down Location https://github.com https://github.my-gh-proxy.com
			header_down Location https://objects.githubusercontent.com https://github.my-gh-proxy.com/ghobjects
			header_down Location https://release-assets.githubusercontent.com https://github.my-gh-proxy.com/ghreleaseassets
			header_down Location https://raw.githubusercontent.com https://github.my-gh-proxy.com/ghraw
			header_down Location https://codeload.github.com https://github.my-gh-proxy.com/ghcodeload

			header_down Set-Cookie "(.*)Domain=\.github\.com(.*)" "$1Domain=.my-gh-proxy.com$2"
		}

		replace {
			stream
			"https://github.com" "https://github.my-gh-proxy.com"
			"https://github.githubassets.com" "https://github.my-gh-proxy.com/ghassets"
			"https://avatars.githubusercontent.com" "https://github.my-gh-proxy.com/ghavatars"
			"https://raw.githubusercontent.com" "https://github.my-gh-proxy.com/ghraw"
			"https://api.github.com" "https://github.my-gh-proxy.com/ghapi"
			"https://codeload.github.com" "https://github.my-gh-proxy.com/ghcodeload"
			"https://objects.githubusercontent.com" "https://github.my-gh-proxy.com/ghobjects"
			"https://release-assets.githubusercontent.com" "https://github.my-gh-proxy.com/ghreleaseassets"
			"https://gist.github.com" "https://github.my-gh-proxy.com/ghgist"
		}
	}
}

要点:

  • handle_path 会自动剥掉前缀再转发:/ghassets/assets/x.css → 上游收到 /assets/x.css,无需手动 rewrite。
  • 主站用无匹配器的 handle 兜底,保证所有未分流路径(含登录、仓库页等)都走 github.com。
  • 各上游的 replace 规则合并进主站块,替换目标统一指向同域路径。
  • 此方案下 assets/avatars/raw/api/codeload/objects/release-assets/gist 这 7 个子域不再需要 DNS 记录和证书,全部流量走一个域名。
编辑此页

目录