Go服务IP总为127.0.0.1的根本原因是Nginx未透传真实IP头,必须配置proxy_set_header X-Real-IP $remote_addr、X-Forwarded-For $proxy_add_x_forwarded_for和X-Forwarded-Proto $scheme三行指令,并在Gin中显式设置TrustedProxies包含Nginx或CDN的可信IP。

Go 服务拿到的 IP 总是 127.0.0.1?先看 Nginx 是否透传了头
根本原因不是 Gin 写错了,而是 Nginx 没把真实 IP 带过来。默认情况下,proxy_pass 只转发请求体和路径,不带任何客户端标识头。必须显式配置三行关键指令,缺一不可:
-
proxy_set_header X-Real-IP $remote_addr;—— 把最外层客户端 IP(直连 Nginx 的那个)塞进X-Real-IP -
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;—— 追加到已有的X-Forwarded-For链,支持多级代理(比如 CDN → Nginx → Go) -
proxy_set_header X-Forwarded-Proto $scheme;—— 方便后端判断是 HTTP 还是 HTTPS,影响重定向或安全头逻辑
注意:$proxy_add_x_forwarded_for 和 $remote_addr 不是变量名,是 Nginx 内置宏,不能写成字符串或漏掉美元符。如果 Nginx 配置里这三行不在 location 或 server 块内、或者被重复覆盖(比如 upstream 里又定义了一次),X-Forwarded-For 就可能为空或只含 Nginx 自己的地址。
Gin 的 c.ClientIP() 为什么有时还是错的
c.ClientIP() 看似开箱即用,但它依赖两个前提:Nginx 真传了头,且你没改过 Gin 的 RemoteIPHeaders 和 TrustedProxies 默认值。Gin 默认只信任 127.0.0.1/8 和 ::1/128 这些本地地址作为可信代理——而你的 Nginx 很可能跑在同机(127.0.0.1)或局域网(如 192.168.1.10),但没显式加入信任列表。
- 如果 Nginx 和 Go 服务在同一台机器,加这一行:
router.TrustedProxies = []string{"127.0.0.1"} - 如果 Nginx 在另一台服务器(比如
10.0.1.5),就得写:router.TrustedProxies = []string{"10.0.1.5"} - 如果用了云负载均衡(如阿里云 SLB),它也会加
X-Forwarded-For,但它的 IP 不在默认信任范围内,必须手动加进TrustedProxies
没设 TrustedProxies,Gin 就会忽略所有 X-Forwarded-For 头,直接 fallback 到 r.RemoteAddr,结果就是 127.0.0.1:34212 这类本地端口地址。
立即学习“go语言免费学习笔记(深入)”;
为什么 X-Forwarded-For 里有多个 IP,取第一个就行?
不是。当请求经过多层代理时(例如用户 → CDN → Nginx → Go),X-Forwarded-For 值可能是 "203.123.45.67, 198.51.100.2, 10.0.1.5"。最左边是原始客户端 IP,中间是各层代理追加的,最右边是直接上游(Nginx)。但前提是每层都正确使用 $proxy_add_x_forwarded_for,而不是简单覆盖为 $remote_addr。
- CDN 如果只写
proxy_set_header X-Forwarded-For $remote_addr;,就会抹掉前面所有 IP,只剩它自己看到的 - Gin 的
c.ClientIP()默认取X-Forwarded-For最左非信任 IP,所以你必须确保TrustedProxies包含所有中间代理(CDN、Nginx 等),否则它会把 CDN 的 IP 当作真实客户端 - 手动解析时别用
strings.Split(header, ",")[0]硬切——得从右往左跳过所有TrustedProxies地址,再取第一个非信任项
Nginx 负载均衡下,ip_hash 会影响真实 IP 获取吗
不影响获取逻辑,但会影响一致性。启用 ip_hash 后,Nginx 会对 $remote_addr 做哈希,把同一客户端固定分发到同一台后端 Go 实例。这里的关键是:$remote_addr 是 Nginx 接收到的 TCP 连接源地址——如果前端还有 CDN 或四层 LB,这个值就不是用户真实 IP,而是 CDN 或 LB 的出口 IP。
- 如果你的流量路径是「用户 → CDN → Nginx → Go」,那
ip_hash实际按 CDN 的 IP 哈希,所有来自同一个 CDN 节点的用户都会打到同一台 Go 服务上,失去用户粒度的粘性 - 要真正按用户 IP 哈希,必须让 CDN 把真实 IP 放进
X-Forwarded-For,然后 Nginx 用map指令提取最左 IP,再基于它做 hash(需要额外配置,非默认行为) - 更稳妥的做法是:关掉
ip_hash,改用 session cookie 或 JWT 携带用户标识,在业务层做状态路由
真实 IP 获取和负载策略是两件事,但它们共享同一个源头——你能否在第一跳(Nginx)就拿到干净、未被覆盖的 $remote_addr 或可靠的 X-Forwarded-For 链。任何中间环节擅自覆盖头、或没声明信任,都会让后续所有环节失效。


















