根本原因是go默认直连不可达的proxy.golang.org;应设goproxy=https://goproxy.cn,direct并配goprivate跳过私有模块代理,同时gosumdb=off绕过校验超时。

为什么 go mod download 总是超时或 403?
根本原因不是网络差,而是 Go 默认直连 proxy.golang.org,而该域名在国内多数地区不可达或被限流;更隐蔽的问题是:即使你配了代理,GO_PROXY 若没设成非空字符串(比如设为 "" 或漏掉),Go 会自动 fallback 到官方代理——而不是走你本地的 http://localhost:8080 或公司内网镜像。
常见错误现象:go mod download 卡住数分钟、报错 proxy.golang.org: dial tcp: i/o timeout,或返回 403 Forbidden(尤其在企业防火墙后)。
-
GO_PROXY必须是非空字符串,设为"direct"表示跳过代理直连模块源(仅适用于私有模块仓库可直通场景) - 多个代理用逗号分隔,Go 会按顺序尝试,直到第一个成功响应(注意:不支持失败重试逻辑,只取首个 2xx 响应)
- 若用自建代理(如 Athens、goproxy.cn),必须确保其支持
/@v/list和/@v/<version>.info</version>等 v1.13+ 的 proxy 协议端点
GO_PROXY 和 GOPRIVATE 必须配对使用
单独设 GO_PROXY 无法解决私有模块问题——Go 会把所有模块(包括 git.company.com/internal/pkg)都扔给代理,而私有仓库通常不允许代理访问,结果就是 403 或 404。这时必须用 GOPRIVATE 明确声明哪些模块跳过代理。
典型配置:
export GO_PROXY=https://goproxy.cn,direct export GOPRIVATE=git.company.com/internal,github.com/myorg
注意:
-
GOPRIVATE是 glob 模式,支持前缀匹配(git.company.com/internal匹配git.company.com/internal/foo,但不匹配git.company.com/external) - 如果私有模块用的是 SSH 地址(如
git@git.company.com:internal/pkg),Go 仍会按 host + path 解析,所以GOPRIVATE写git.company.com/internal就够了 - 一旦设了
GOPRIVATE,对应模块将完全绕过GO_PROXY,改用git或https直接拉取,因此确保你的git配置(如~/.gitconfig中的 credential helper)能正常认证
调试代理链实际生效路径的三个命令
别猜,用 Go 自带工具验证代理是否真被调用:
- 运行
go env GO_PROXY GOPRIVATE确认环境变量已加载(注意 shell 是否 reload 过 profile) - 加
-x参数看详细请求:go list -m -f '{{.Dir}}' github.com/go-sql-driver/mysql -x,输出里会出现类似GET https://goproxy.cn/github.com/go-sql-driver/mysql/@v/v1.7.0.info的行 - 临时禁用代理验证 fallback 行为:
GO_PROXY=direct go mod download github.com/go-sql-driver/mysql,若成功说明模块源本身可达,问题纯出在代理链配置
容易踩的坑:某些 IDE(如 Goland)会继承系统环境变量,但终端启动方式不同(如从 GUI 启动的终端可能未读取 ~/.zshrc),导致 go env 输出和 IDE 内行为不一致。
公司内网场景下必须关掉 GOSUMDB
即使代理链通畅,go mod download 仍可能卡在 checksum 验证阶段——因为 GOSUMDB 默认指向 sum.golang.org,该服务同样受网络限制,且不支持代理链(Go 不会用 GO_PROXY 访问它)。
解决方案只有两个:
- 设
GOSUMDB=off(开发/内网环境最常用,跳过校验) - 设
GOSUMDB=sum.golang.org+https://sum.golang.google.cn(国内可用镜像,但需确认其同步时效性)
注意:GOSUMDB=off 不影响模块下载,只跳过 go.sum 文件的远程校验;只要你不手动篡改 go.sum,安全性风险可控。生产构建流水线若要求强校验,才需要部署私有 sumdb 或用 air-gapped 方式预置 checksum。
真正麻烦的是混合场景:既有公有模块(走代理)、又有私有模块(直连)、还要绕过 sumdb 校验——这三者必须同时配对生效,缺一不可。漏掉任意一个,go mod tidy 就可能卡在某个环节不动。











