golang获取客户端真实 IP
获取客户端 IP 看似简单,实际涉及网络架构与安全信任两个维度的权衡。服务是否经过代理、代理是否可信,直接决定了获取策略。
1. IP 获取优先级
以下是获取客户端真实 IP 的标准优先级,适用于部署在可信代理(Nginx、AWS ELB、Cloudflare 等)后的服务:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#4F46E5', 'primaryTextColor': '#000', 'primaryBorderColor': '#3730A3', 'lineColor': '#6366F1', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
A(["请求到达"]) --> B{"X-Forwarded-For 存在?"}
B -->|"是"| C["取第一个 IP"]
B -->|"否"| D{"X-Real-IP 存在?"}
D -->|"是"| E["使用 X-Real-IP"]
D -->|"否"| F["使用 RemoteAddr"]
C --> G(["返回客户端 IP"])
E --> G
F --> G
classDef primary fill:#4F46E5,stroke:#3730A3,color:#fff
classDef success fill:#10B981,stroke:#059669,color:#fff
classDef decision fill:#F59E0B,stroke:#D97706,color:#000
class A,G primary
class C,E,F success
class B,D decision1.1 实现代码
1 | package main |
2. 三种 IP 来源详解
2.1 X-Forwarded-For
标准 HTTP 头,记录请求链路中所有节点的 IP。
| 属性 | 说明 |
|---|---|
| 格式 | ClientIP, Proxy1, Proxy2 |
| 特点 | 每级代理追加自身 IP 到列表末尾 |
| 取值 | 通常取第一个作为原始客户端 IP |
| 风险 | 可被伪造,需结合可信代理使用 |
2.2 X-Real-IP
约定俗成的 HTTP 头,由 Nginx 等反向代理设置。
Nginx 典型配置:
1 | proxy_set_header X-Real-IP $remote_addr; |
如果服务直接暴露公网,用户可伪造此头。只有经过可信代理(会覆盖伪造值)时,该值才可信。
2.3 RemoteAddr
TCP 层信息,代表直接连接方的 IP。
| 场景 | RemoteAddr 值 |
|---|---|
| 用户直连 | 用户真实 IP,不可伪造 |
| 经过代理 | 代理服务器 IP(如 127.0.0.1) |
3. 安全最佳实践
盲目信任 X-Forwarded-For 或 X-Real-IP 存在安全风险,只有当服务运行在可信代理后时,才应信任这些 Header。
trustedProxies 的核心含义就是:” 哪些上游节点(代理/负载均衡)是我自己控制的、可信的 “。
只有当请求确实是从这些可信节点转发过来的时候,你才去相信 X-Forwarded-For / X-Real-IP / Forwarded 这些头;否则这些头很容易被客户端自己伪造。
3.1 Nginx
在你把 nginx 放在业务服务前面并用 proxy_pass 转发时,nginx 就是反向代理(reverse proxy)。
流程大概是: 客户端 → nginx → 你的 Go 服务。此时 Go 服务看到的 TCP 连接是 “nginx → Go”,所以 r.RemoteAddr 代表的是 nginx 的 IP(而不是客户端)。
- 用了 nginx,remoteIP 一定是 127.0.0.1 吗
- nginx 和 Go 在同一台机器,nginx 转发到
127.0.0.1:xxxx,这时 Go 收到的连接来自本机,所以很可能:r.RemoteAddr 是 127.0.0.1: 端口(IPv4) - nginx 和 Go 不在同一台机器,r.RemoteAddr 会是 nginx 的内网 IP(例如 10.x、172.16.x、192.168.x),而不是 127.0.0.1
- nginx 和 Go 在同一台机器,nginx 转发到
- nginx 怎么配,才能把真实 IP 传给 Go?
1 | proxy_set_header X-Real-IP $remote_addr; |
3.2 为什么要 trustedProxies
因为如果你在 Go 里 “ 无条件信任 X-Forwarded-For”,攻击者可以直接发请求加个头:X-Forwarded-For: 1.2.3.4。你就会被骗,以为他来自 1.2.3.4。
- 如果请求是从 nginx(可信代理)来的 → 才读取
X-Forwarded-For / X-Real-IP - 如果请求不是从可信代理来的 → 只用
RemoteAddr
trustedProxies 在 Go 里应该填什么?
一般填 “ 可能作为最后一跳连接到你 Go 服务的代理的 IP/网段 “:
- 如果 nginx 和 Go 同机,并且走 127.0.0.1: ,把
127.0.0.0/8(以及可能的::1/128)作为 trusted - 如果 nginx 在内网: 把 nginx 的内网网段或具体 IP 加进去
- 如果你的 Go 前面还有一层 Envoy/Ingress: 也要把那层的网段加进去(因为 Go 的 RemoteAddr 看到的是它)
最佳实践:尽量填具体的代理网段/地址,不要偷懒 “ 信任所有内网 “,除非你很确定网络边界干净。
4. IP 地理定位库
获取 IP 后,可通过 GeoIP 库解析地理位置。推荐使用 MaxMind 的 GeoLite2 数据库:
- City 库:精度更高,包含城市级别信息
- Country 库:仅国家级别
资源地址:GeoLite.mmdb 镜像