在构建现代分布式系统或进行网络调试时,准确获取客户端 IP 地址是至关重要的环节,由于 NAT(网络地址转换)、反向代理(如 Nginx、HAProxy)、负载均衡器(如 AWS ELB、阿里云 SLB)以及 CDN 的存在,HTTP 请求到达后端服务器时,源 IP 往往不再是客户端的真实 IP。

以下将详细解析 HTTP 服务器与客户端 IP 的关系、常见获取方式、潜在风险及最佳实践。
核心概念:为什么获取真实 IP 如此复杂?
在标准的 TCP/IP 连接中,服务器通过 socket.getpeername() 或类似 API 获取的是直接建立 TCP 连接的上一跳 IP 地址。
- 无代理环境:客户端直连服务器,获取到的即为客户端真实 IP。
- 有代理环境:客户端 -> 反向代理/负载均衡器 -> 服务器,服务器看到的“客户端 IP”实际上是代理服务器的内网 IP 或负载均衡器的 VIP(虚拟 IP)。
为了穿透这些中间层,HTTP 协议本身并没有强制规定如何传递真实 IP,而是依赖业界约定俗成的 HTTP Header 字段。
常见的获取真实 IP 的 HTTP Header
以下是处理真实 IP 时最常涉及的几个 Header,它们的优先级和可靠性各不相同:
| Header 名称 | 描述 | 可靠性 | 备注 |
|---|---|---|---|
| X-Forwarded-For (XFF) | 最通用的标准,格式为 client, proxy1, proxy2。 |
中 | 客户端可伪造,需信任上游代理。 |
| X-Real-IP | 通常由 Nginx 等反向代理设置,仅包含客户端真实 IP。 | 高 | 取决于代理配置,非 HTTP 标准。 |
| CF-Connecting-IP | Cloudflare 专用 Header。 | 高 | 仅限使用 Cloudflare CDN 的场景。 |
| True-Client-IP | Akamai 等 CDN 专用 Header。 | 高 | 仅限特定 CDN 提供商。 |
| X-Forwarded-Proto | 指示原始请求是 HTTP 还是 HTTPS。 | 高 | 用于判断协议,非 IP 地址。 |
解析 X-Forwarded-For (XFF) 的详细机制
X-Forwarded-For 是最常用的字段,其结构如下:
X-Forwarded-For: client, proxy1, proxy2
- client: 发起请求的原始客户端 IP。
- proxy1: 第一个代理服务器的 IP。
- proxy2: 第二个代理服务器的 IP(如果有)。
关键规则:

- 从左到右读取:最左边的 IP 通常被认为是客户端 IP。
- 信任链:服务器必须信任直接连接自己的代理(如 Nginx)所附加的 Header,如果服务器直接面对公网,且未配置信任任何代理,则应忽略 XFF,直接使用 TCP 连接 IP。
- 伪造风险:恶意用户可以手动设置
X-Forwarded-For: 1.2.3.4。永远不要完全信任客户端发送的 XFF 头,除非你确定请求经过了受信任的代理层。
不同技术栈下的实现示例
Nginx 配置示例
在 Nginx 中,通常通过 proxy_set_header 来传递真实 IP:
location / {
proxy_pass http://backend_server;
# 设置 X-Real-IP
proxy_set_header X-Real-IP $remote_addr;
# 设置 X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Node.js (Express) 获取真实 IP
// 注意:trust proxy 必须设置为 true,否则 req.ip 可能返回代理 IP
app.set('trust proxy', true);
app.get('/', (req, res) => {
// req.ip 会自动解析 X-Forwarded-For 或 X-Real-IP
const clientIP = req.ip;
console.log('Client IP:', clientIP);
res.send(`Your IP is: ${clientIP}`);
});
Java (Spring Boot) 获取真实 IP
@RestController
public class IpController {
@GetMapping("/ip")
public String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 处理 X-Forwarded-For 可能包含多个 IP 的情况
if (ip != null && ip.length() > 0 && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return "Client IP: " + ip;
}
}
安全最佳实践与注意事项
-
信任代理配置:
- 在应用服务器(如 Tomcat, Nginx, Node.js)中,必须正确配置“信任代理”列表,在 Spring Boot 中设置
server.forward-headers-strategy=framework或在 Nginx 中确保只信任内部网络 IP 附加的 Header。 - 危险操作:如果未配置信任代理,恶意用户可通过发送
X-Forwarded-For: 127.0.0.1绕过基于 IP 的访问控制(如防火墙规则、登录限制)。
- 在应用服务器(如 Tomcat, Nginx, Node.js)中,必须正确配置“信任代理”列表,在 Spring Boot 中设置
-
日志记录规范:
- 在访问日志中,建议同时记录
remote_addr(直接连接 IP)和X-Forwarded-For(真实 IP),以便审计和排查问题。 - 示例 Nginx 日志格式:
log_format main '$remote_addr $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
- 在访问日志中,建议同时记录
-
IPv6 兼容性:
- 随着 IPv6 的普及,确保代码能正确处理 IPv6 地址格式(如
:1,2001:db8::1)。 X-Forwarded-For同样支持 IPv6,格式与 IPv4 一致。
- 随着 IPv6 的普及,确保代码能正确处理 IPv6 地址格式(如
-
CDN 场景:
- 如果使用 Cloudflare、AWS CloudFront 等 CDN,务必使用其提供的专用 Header(如
CF-Connecting-IP),因为这些 CDN 可能会修改或丢弃标准的X-Forwarded-For。
- 如果使用 Cloudflare、AWS CloudFront 等 CDN,务必使用其提供的专用 Header(如
常见问题与解答
Q1: 为什么我的服务器获取到的 IP 总是 127.0.0.1 或内网 IP?

A: 这通常是因为请求经过了反向代理(如 Nginx、HAProxy)或负载均衡器,这些中间件建立了与后端服务器的新 TCP 连接,因此后端服务器看到的源 IP 是代理服务器的 IP。
- 解决方案:
- 检查代理配置,确保其设置了
X-Forwarded-For或X-Real-IPHeader。 - 在后端应用中启用“信任代理”功能,使其能够解析这些 Header 并提取真实客户端 IP。
- 如果希望后端直接获取客户端 IP,可考虑使用 TCP 代理模式(如 L4 负载均衡)而非 HTTP 代理模式(L7),但这会失去 HTTP 层的功能(如 SSL 终止、缓存等)。
- 检查代理配置,确保其设置了
Q2: 如何防止用户伪造 X-Forwarded-For Header?
A: 完全防止伪造是不可能的,因为 Header 由客户端发送,但可以通过以下策略降低风险:
- 信任链验证:只在应用服务器信任的代理层(如 Nginx)之后才解析 XFF,如果请求直接来自公网且未经过受信任代理,则忽略 XFF,直接使用 TCP 连接 IP。
- 使用专用 Header:对于使用 CDN 的场景,使用 CDN 提供的专用 Header(如
CF-Connecting-IP),因为 CDN 会在边缘节点验证并注入该 Header,客户端无法轻易伪造且 CDN 会忽略客户端发送的同名 Header。 - IP 白名单/黑名单:结合业务逻辑,对异常 IP 段或已知恶意 IP 进行拦截,而不是单纯依赖 Header 内容。
- 日志审计:记录所有请求的
remote_addr和X-Forwarded-For,定期分析是否有不一致的伪造行为。
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/494366.html