c.ClientIP()在生产环境几乎必然返回错误IP,因其默认仅信任127.0.0.1/8等本地段,而Nginx/SLB/CDN等真实代理未被信任,导致回退至RemoteAddr(如10.0.1.5或127.0.0.1),须同步配置Nginx透传头、Gin可信代理段及IP合法性校验。

c.ClientIP() 在生产环境几乎必然返回错误 IP,除非你同时搞定 Nginx 透传 + Gin 可信代理配置 + IP 合法性校验三件事。单独调用它没意义。
为什么 c.ClientIP() 总是返回 127.0.0.1 或内网地址
c.ClientIP() 不是从 TCP 连接里“抓”真实 IP,而是按顺序查 X-Forwarded-For → X-Real-IP → c.Request.RemoteAddr。但默认只信任 127.0.0.1/8 和 ::1/128 这些本地段——而你的 Nginx、SLB、CDN 都不在这个范围内,整条头链被忽略,直接 fallback 到 RemoteAddr(比如 10.0.1.5 或 100.x.x.x)。
常见现象包括:
- 本地开发时 Nginx 和 Go 同机,但没把
127.0.0.1加进可信列表,c.ClientIP()返回127.0.0.1 - K8s NodePort 下返回
10.0.x.x(externalTrafficPolicy: Cluster导致 SNAT) - 阿里云 SLB 后返回
100.x.x.x(SLB 出口 IP 没加进SetTrustedProxies)
Nginx 必须配这三行,缺一不可
仅 Gin 端配置无法补救上游没传头的问题。Nginx 的 location 或 server 块里必须有:
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;
注意点:
-
$proxy_add_x_forwarded_for是 Nginx 内置变量,不是字符串字面量;写成"$remote_addr"或漏掉$都会失效 - 如果用了 CDN 或 WAF,它们也会追加
X-Forwarded-For,此时 Nginx 要确保自己是“最后一级可信代理”,否则可能把 CDN 的 IP 当作客户端 IP - 不要在
upstream块里重复定义这些proxy_set_header,优先级错乱会导致头被覆盖
Gin 启动时必须显式设置可信代理段
在 gin.Default() 或 gin.New() 之后、r.Run() 之前,立即执行:
r := gin.Default()
err := r.SetTrustedProxies([]string{"172.19.10.12", "100.64.0.0/10", "192.168.0.0/16"})
if err != nil {
log.Fatal("set trusted proxies failed:", err)
}
关键细节:
- 不能留空或传
nil,否则默认信任所有代理(不安全) - 必须用 CIDR 表示法(如
"100.64.0.0/10"),不能只写单个 IP("100.64.0.1"会被拒绝) - 云厂商 SLB、Ingress Controller、WAF 的出口网段必须全部列进去,漏一个就 fallback 到内网地址
- 若用 Kubernetes Ingress(如 nginx-ingress),需查其 Pod 所在节点的网段或 Service 的 ClusterIP 段(通常为
10.96.0.0/12)
如何验证真实 IP 是否生效
别只靠日志打印 c.ClientIP(),要分层验证:
- curl -H "X-Forwarded-For: 203.208.60.1, 192.168.1.100" http://yourdomain/ping —— 观察是否返回
203.208.60.1 - 检查 Nginx access_log 中
$http_x_forwarded_for字段是否含多段 IP,且最左是你期望的真实出口 IP - 在 Gin 中加中间件打点:
log.Printf("RemoteAddr=%s, XFF=%s, ClientIP=%s", c.Request.RemoteAddr, c.Request.Header.Get("X-Forwarded-For"), c.ClientIP()) - 若仍不对,优先怀疑:Nginx 没传头 / Gin
SetTrustedProxies漏了某段 / 真实 IP 是私有地址(如192.168.x.x)被自动丢弃(Gin 默认会过滤非法 IP)
最容易被忽略的是:Gin 对 X-Forwarded-For 中的 IP 会做合法性校验,若解析出的 IP 是 RFC 1918 私有地址(且未显式启用 engine.ForwardedByClientIP = true),它会跳过该值直接 fallback——这不是 bug,是设计行为。


















