req.RemoteAddr 不可靠,因它仅返回直连代理IP(如Nginx、SLB或CDN节点内网地址),而非用户真实IP;生产中须校验可信代理后解析X-Forwarded-For,取最左非私有IP。

直接用 req.RemoteAddr 拿到的几乎从来不是真实用户 IP,除非你的服务完全不经过任何代理、CDN 或云负载均衡,直连公网。生产环境里,必须校验可信代理后再解析请求头,否则限流、风控、日志全会出错。
为什么 req.RemoteAddr 不可靠
它只返回和 Go 服务**直连的对端地址**——通常是 Nginx、K8s Ingress、阿里云 SLB 或 CDN 边缘节点的内网 IP。比如看到 10.0.1.5:42193,这其实是你集群里某个反向代理的出口地址,不是用户本身。
- 本地开发用
curl localhost:8080,它甚至可能是127.0.0.1 - CDN 场景下,
req.RemoteAddr是 CDN 节点 IP,不是访客真实出口 IP - 哪怕 Nginx 配了
proxy_set_header X-Real-IP $remote_addr,没做代理信任校验,X-Real-IP也照样能被伪造
Go 标准库怎么安全提取真实 IP
别依赖第三方包(如 github.com/mholt/xforwarded),它们默认信任所有代理或配置复杂。自己写一个 20 行内的函数更可控、更易审计。
- 先从
req.RemoteAddr解析出直连 IP(去掉端口) - 调用自定义
isTrustedProxy(ip string)判断该 IP 是否属于你信任的代理网段(如"10.0.0.0/8"、"192.168.1.0/24") - 只有可信时,才解析
X-Forwarded-For:按逗号切分、逐个strings.TrimSpace、用net.ParseIP(ip).IsPrivate()排除私有地址(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16) - 取最左第一个非私有 IP;若全私有,再 fallback 到
X-Real-IP(前提是 Nginx 确实配了proxy_set_header X-Real-IP $remote_addr)
Gin 框架的 c.ClientIP() 怎么用才有效
这个方法本身逻辑合理,但默认只信任 127.0.0.1。如果你的 Nginx 在另一台机器上,或者用了云厂商 SLB,它就直接失效。
- 必须显式调用
r.SetTrustedProxies([]string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}) - 不要用
r.SetTrustedProxies(nil)或["0.0.0.0/0"],这等于放弃校验,把伪造入口彻底打开 - 如果代理是 IPv6,记得加
::1和对应 IPv6 网段(如"fd00::/8") -
c.ClientIP()内部仍会 fallback 到req.RemoteAddr,所以代理网段漏配时,你会拿到 SLB 内网 IP,而不是报错或空值
最容易被忽略的一点:Nginx 的 proxy_set_header X-Forwarded-For 必须用 $proxy_add_x_forwarded_for,而不是 $remote_addr。前者保留原有链路,后者会覆盖整个头,导致原始客户端 IP 丢失。真实链路中,XFF 字段的顺序和完整性,比“有没有这个 header”更重要。


















