Gin默认不信任代理头,因其依赖标准库的http.Request原始字段,只认直连RemoteAddr;需显式调用gin.SetTrustedProxies()声明可信代理并配合Nginx正确设置X-Forwarded-*头,否则ClientIP、TLS判断等均会出错。

直接部署 Gin 服务本身很简单,但要让它在生产环境里被 Nginx 正确代理并透传关键请求头(比如 X-Forwarded-For、X-Real-IP、X-Forwarded-Proto),必须同时改 Gin 和 Nginx 两端——只配一端,c.ClientIP() 会拿错 IP,c.Request.TLS 会误判 HTTPS,日志和鉴权都可能出问题。
为什么 Gin 默认不信任代理头
Gin 的 c.ClientIP()、c.IsWebsocket() 等方法依赖 http.Request 原始字段,而标准库默认不解析 X-Forwarded-* 这类代理头。它只认直连客户端的 RemoteAddr。Nginx 转发后,Gin 看到的是 Nginx 的内网 IP(如 127.0.0.1),不是真实用户 IP。
解决方式不是让 Gin “自动识别”,而是明确告诉它哪些 IP 是可信代理:
- 调用
gin.SetTrustedProxies()显式声明可信代理网段(如[]string{"127.0.0.1", "10.0.0.0/8"}) - 确保 Nginx 配置中设置了
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;等头 - 若用 Docker 或 Kubernetes,注意宿主机 IP、Service ClusterIP、Ingress Controller IP 都要加入可信列表
Nginx proxy_pass 必须带斜杠结尾
proxy_pass 的末尾斜杠决定路径重写行为,直接影响 Gin 路由匹配。常见错误配置:
location /api/ {
proxy_pass http://127.0.0.1:8080; # ❌ 少了 /,/api/user → 后端收到 /api/user
}这会导致 Gin 收到带 /api 前缀的请求,但你的路由注册的是 /user,404。
正确写法:
location /api/ {
proxy_pass http://127.0.0.1:8080/; # ✅ 末尾 / 触发路径剥离:/api/user → /user
proxy_set_header Host $host;
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;
}其他要点:
- 如果 Gin 服务监听
0.0.0.0:8080,别用localhost(可能走 IPv6 回环或 DNS 解析失败),用127.0.0.1 -
proxy_set_header Host必须设,否则 Gin 的c.Request.Host可能为空或为 IP - WebSocket 场景还需加
proxy_http_version 1.1;和proxy_set_header Upgrade $http_upgrade;
Gin 启动时禁用调试模式并绑定正确地址
gin.Default() 在开发时方便,但生产环境必须关掉调试输出和 panic 页面:
- 调用
gin.SetMode(gin.ReleaseMode),否则c.AbortWithStatusJSON(500, ...)可能泄露堆栈 -
r.Run()不传参数会监听:8080,但若 Nginx 已占 80 端口,Gin 应该只监听内网端口(如127.0.0.1:8080),避免暴露给外网 - 用
http.Server显式构造更可控:&http.Server{Addr: "127.0.0.1:8080", Handler: r}
示例片段:
if os.Getenv("GIN_MODE") == "release" {
gin.SetMode(gin.ReleaseMode)
}
r := gin.New()
r.Use(gin.Recovery(), middleware.Logger()) // 自定义日志中间件应记录 X-Real-IP
<p>// 设置可信代理(Nginx 所在机器的 IP)
if err := gin.SetTrustedProxies([]string{"127.0.0.1"}); err != nil {
log.Fatal(err)
}</p><p>srv := &http.Server{
Addr: "127.0.0.1:8080",
Handler: r,
}
log.Fatal(srv.ListenAndServe())最易忽略的一点:Nginx 的 proxy_set_header X-Forwarded-Proto $scheme; 和 Gin 的 c.Request.TLS 判断是解耦的——Gin 不会自动根据这个 header 改 TLS 字段,所以鉴权逻辑里判断 HTTPS,必须自己读 header:c.GetHeader("X-Forwarded-Proto") == "https",而不是依赖 c.Request.TLS != nil。


















