Go服务必须绑定127.0.0.1而非0.0.0.0,否则绕过Nginx导致TLS终止失效;proxy_pass末尾必须加/以正确重写路径,避免404;upstream须独立定义并配置健康检查,否则无法实现负载均衡与故障剔除。

Go 服务必须绑定 127.0.0.1,否则 Nginx 反向代理会失效;Nginx 的 upstream 块和 proxy_pass 末尾斜杠是两个最常导致 502/404 的硬伤。
Go 启动时监听地址必须是 127.0.0.1,不能用 :8080 或 0.0.0.0:8080
Go 程序若写成 http.ListenAndServe(":8080", handler),等价于监听 0.0.0.0:8080——它把服务直接暴露在所有网卡上,绕过 Nginx 的 TLS 终止、限流和健康检查路径。Nginx 的 upstream 探测请求(如 /healthz)可能走内网路由,但服务响应了外网请求,导致探测失败、节点被剔除。
正确写法是显式绑定回环地址:
router.Run("127.0.0.1:8080")
或更稳妥地用 http.Server 结构体:
立即学习“go语言免费学习笔记(深入)”;
server := &http.Server{Addr: "127.0.0.1:8080", Handler: router}
server.ListenAndServe()
- Docker 场景下,
127.0.0.1在容器内只指向自身,Nginx 若在另一容器中,需改用服务名(如go-app:8080)或host.docker.internal - 别依赖 Gin 的默认端口行为,始终显式传入地址字符串
- 启动前加日志输出
fmt.Printf("listening on %s\n", addr),确认实际绑定地址
proxy_pass 末尾必须带 /,否则 Go 路由全量 404
Nginx 的路径重写规则很机械:location /api/ + proxy_pass http://backend/;(结尾有 /)→ 请求 /api/v1/users 被重写为 /v1/users 发往后端;而 proxy_pass http://backend;(无 /)→ 原样拼成 /api/v1/users,但 Gin 默认注册的是 GET /v1/users,不匹配就 404。
绝大多数 Gin 项目路由从根路径起步,所以必须加 /:
location / {
proxy_pass http://go_backend/;
}
- 如果后端确实需要保留前缀(比如前端静态资源统一挂
/static/),才考虑不加/,但需同步调整 Gin 的 Group 路由前缀 - 不要用变量拼接
proxy_pass(如proxy_pass http://$backend/;),Nginx 会禁用路径替换逻辑 - 用
curl -v http://localhost/api/version配合 Nginxerror_log查看实际转发路径
upstream 必须独立定义,不能直接写死在 proxy_pass 里
把 proxy_pass http://127.0.0.1:8080; 直接塞进 location 块,等于放弃负载均衡能力:无法多实例、无健康检查、宕机节点不会自动剔除。
真正可用的配置必须用命名 upstream 块:
upstream go_backend {
server 127.0.0.1:8080 max_fails=1 fail_timeout=10s;
server 127.0.0.1:8081 max_fails=1 fail_timeout=10s;
keepalive 32;
}
server {
location / {
proxy_pass http://go_backend/;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}
}
-
max_fails=1太敏感,Go 进程一旦 panic 就退出,Nginx 10 秒内探测失败 1 次就踢掉;建议搭配 systemd 的Restart=always或容器自动重启 -
proxy_next_upstream必须显式列出错误类型,否则慢查询或 GC STW 卡住时,Nginx 不会切到下一个节点 - 轮询是默认策略,无需写
round-robin;要权重就加weight=2,要 IP 亲和就加ip_hash,但得同步配proxy_set_header X-Real-IP $remote_addr,否则后端拿不到真实 IP
Gin 获取真实客户端 IP 必须靠 Nginx 透传头,不能信 r.RemoteAddr
Nginx 默认把请求转给 Go 时,r.RemoteAddr 是 127.0.0.1:54321 这类本地地址。Gin 的 c.ClientIP() 底层也是读 X-Real-IP 或 X-Forwarded-For,前提是 Nginx 真传了。
Nginx 配置里这三行不能少:
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;
-
X-Forwarded-For可能是逗号分隔链(如203.0.113.195, 198.51.100.100),c.ClientIP()会取第一个非私有地址,但前提是链未被伪造;生产环境建议在 Nginx 层做可信来源限制 - 如果用了
ip_hash,又没传X-Real-IP,所有请求都会被当成来自 Nginx 本机,失去分流意义 - 验证方法:在 Gin 中加个接口打印
c.Request.Header.Get("X-Real-IP")和c.ClientIP(),对比是否一致


















