Nginx proxy_pass末尾斜杠决定路径替换行为:带/则剥离location匹配前缀后拼接,不带/则原样拼接;Go服务默认监听根路径,故推荐proxy_pass结尾加/,否则路径错位导致404。

直接说结论:Nginx 本身不感知 Go 语言,它只按 HTTP 协议转发请求;配置关键不在 Go 代码里,而在 upstream 块和 proxy_pass 指令的组合是否匹配后端服务的实际监听地址与路径逻辑。
为什么 proxy_pass 末尾加不加斜杠会 404?
这是最常踩的坑。Nginx 的 proxy_pass 对路径拼接有严格规则:
- 如果
location /api/+proxy_pass http://backend/;(结尾有/),则请求/api/v1/users会被重写为/v1/users发给后端 - 如果
location /api/+proxy_pass http://backend;(结尾无/),则原样拼接成/api/v1/users—— 后端若没注册/api/...路由,就 404 - Go 项目(如 Iris、Gin、Echo)默认路由是根路径起步的,比如
GET /version,所以推荐在proxy_pass末尾加/,避免路径错位
如何让多个 Go 实例被 Nginx 均衡调度?
核心是定义 upstream 块,并确保每个 Go 实例监听不同端口且能独立健康运行:
- 启动两个 Go 服务:
./myapp -port=8081和./myapp -port=8082,都监听localhost(不要用127.0.0.1在某些 Docker 场景下可能解析失败) - Nginx 配置中
upstream必须显式命名,例如upstream go_backend,不能直接写在proxy_pass里 - 轮询是默认策略,无需额外关键字;若需权重,写成
server localhost:8081 weight=2; - 别忘了加
health_check(需 Nginx Plus)或用开源模块如nginx_upstream_check_module,否则宕机实例不会自动剔除
Go 服务怎么拿到真实客户端 IP?
Go 应用默认从 RemoteAddr 拿到的是 Nginx 的本地地址(如 127.0.0.1:54321),必须靠 Nginx 透传并 Go 主动读取请求头:
立即学习“go语言免费学习笔记(深入)”;
- 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; - Go 代码中不能直接用
r.RemoteAddr,而要检查r.Header.Get("X-Real-IP")或r.Header.Get("X-Forwarded-For")(注意后者可能是逗号分隔的链) - 如果你用 Gin,可用
c.ClientIP();Iris 用ctx.RemoteIP()—— 它们底层都做了头解析封装,但前提是 Nginx 真传了
为什么 HTTPS 终止放在 Nginx 而不是 Go 服务里?
不是不能,而是不推荐 —— 尤其在微服务架构中:
- Go 标准库的 TLS 处理比 Nginx 重,每个连接都要做完整握手,高并发下 CPU 开销明显
- 证书更新、OCSP Stapling、HSTS、ALPN 等功能 Nginx 原生支持,Go 里得自己调
crypto/tls拼,容易出错 - 更关键的是:一旦你在 Go 里启用 HTTPS,你就失去了用 Nginx 做统一限流、WAF、缓存静态资源的能力
- 正确做法是 Nginx 配 SSL,后端 Go 只走 HTTP(
proxy_pass http://go_backend),既安全又轻量
真正麻烦的从来不是写几行 Nginx 配置,而是确认 Go 服务暴露的端口、路径前缀、健康检查接口是否和 upstream 中的 server 条目完全对齐;少一个 /,多一个空格,或者端口没监听成功,整个链路就静默失败。


















