Java 获取 Request IP 地址终极指南(从入门到精通,含Nginx反向代理等真实场景)
** 还在为获取客户端真实IP而烦恼?本文彻底讲透 HttpServletRequest 中IP获取的各种姿势、常见陷阱及解决方案,助你轻松应对生产环境复杂挑战。

(Meta Description)
本文是Java开发者获取HTTP请求IP的权威指南,详细讲解了如何从HttpServletRequest对象中获取IP地址,深入剖析了代理服务器(如Nginx)、负载均衡等真实场景下IP获取的难点,并提供了完整、健壮的代码解决方案,无论你是Java新手还是资深工程师,都能在这里找到你需要的答案。
引言:为什么获取客户端真实IP如此重要?
在Web开发中,获取客户端的真实IP地址是一项非常常见且重要的需求,它广泛应用于以下场景:
- 用户行为分析: 统计用户地域分布、访问频率,构建用户画像。
- 安全防护: 记录登录IP、操作日志,用于异常登录检测和追溯。
- 内容个性化: 根据用户所在地区提供差异化的内容或服务。
- 反爬虫策略: 识别恶意爬虫,进行封禁或限制。
这个看似简单的任务,在生产环境中却暗藏玄机,当你使用了Nginx、Apache、SLB(负载均衡器)等代理或反向代理后,直接获取到的IP往往是代理服务器的IP,而非客户端的真实IP,本文将带你彻底搞懂这个问题。
基础篇:从 HttpServletRequest 中获取IP
在Java Web开发中,我们通常通过 HttpServletRequest 对象来获取请求信息,获取IP最直接的方法是调用 getRemoteAddr()。

核心方法:request.getRemoteAddr()
这是获取客户端IP地址最基本、最常用的方法。
import javax.servlet.http.HttpServletRequest;
public String getClientIp(HttpServletRequest request) {
return request.getRemoteAddr();
}
工作原理:
getRemoteAddr() 返回的是客户端 socket 连套接字的地址,在“理想”的直连环境中(即客户端直接请求你的Tomcat/Nginx),这个方法确实能返回客户端的真实IP。
示例:
客户端浏览器 -> 你的服务器(IP: 123.45.67.89)
request.getRemoteAddr() 返回的结果就是 45.67.89。
进阶篇:穿透代理,获取真实IP
现实世界中,很少有应用是“裸奔”的,请求会经过一层或多层代理。
代理服务器与 X-Forwarded-For 头
当一个请求通过代理服务器时,代理服务器会在HTTP请求头中添加一个 X-Forwarded-For (XFF) 字段,用来记录原始客户端IP以及经过的中间代理IP。
X-Forwarded-For 的格式:
X-Forwarded-For: client_ip, proxy1_ip, proxy2_ip, ...
client_ip:最初发起请求的客户端IP。proxy1_ip,proxy2_ip:依次经过的代理服务器IP。
注意: X-Forwarded-For 头可以被伪造,因此不能完全信任,但在大多数由可信基础设施(如公司内网Nginx、云服务商LB)管理的场景下,它是获取真实IP最可靠的依据。
另一个重要的头:X-Real-IP
一些Web服务器(如Nginx)还会设置 X-Real-IP 头,它通常直接记录客户端的真实IP,而没有经过代理链。
X-Real-IP 的格式:
X-Real-IP: client_ip
对比:
X-Forwarded-For:是一个IP列表,记录了完整路径。X-Real-IP:通常是单个IP,代表最初的客户端IP。
还有一个:Proxy-Client-IP 和 WL-Proxy-Client-IP
这些是特定Web服务器(如WebLogic)添加的请求头,原理与 X-Forwarded-For 类似,用于标识经过其代理的客户端IP。
实战篇:构建一个健壮的IP获取工具类
既然有多种可能性,我们应该如何编写一个既能处理直连请求,又能穿透代理的健壮方法呢?
核心逻辑(黄金法则):
- 优先检查
X-Forwarded-For头。 如果存在,则取其第一个非未知IP(即列表最左边的IP,也就是最初的客户端IP)。 X-Forwarded-For为空或无效,则检查X-Real-IP头。- 如果以上两者都为空,则回退到
request.getRemoteAddr()。 - 安全性考虑: 所有从请求头获取的IP都需要进行校验,防止伪造和攻击。
下面是一个经过精心设计的、可直接用于生产环境的工具类:
IpUtil.java
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
public class IpUtil {
/**
* 获取客户端IP地址
* 支持多级代理,从最左边的第一个非unknown的IP开始,作为客户端的IP
*
* @param request HttpServletRequest
* @return 客户端真实IP地址
*/
public static String getClientIpAddress(HttpServletRequest request) {
String ip = null;
// 1. 从X-Forwarded-For头获取
ip = request.getHeader("X-Forwarded-For");
if (!isValidIp(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (!isValidIp(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (!isValidIp(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (!isValidIp(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
// 2. 如果上述头信息都没有,或者IP无效,则使用getRemoteAddr()
if (!isValidIp(ip)) {
ip = request.getRemoteAddr();
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (StringUtils.isNotEmpty(ip) && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
/**
* 校验IP是否有效
*
* @param ip IP地址
* @return 是否有效
*/
private static boolean isValidIp(String ip) {
return StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip);
}
}
代码解析:
- 优先级顺序: 代码按照
X-Forwarded-For->Proxy-Client-IP->WL-Proxy-Client-IP->HTTP_CLIENT_IP->HTTP_X_FORWARDED_FOR->getRemoteAddr()的顺序查找。 isValidIp方法: 一个简单的辅助方法,确保我们处理的是非空且不是 "unknown" 的字符串,避免了无效数据干扰。- 处理多IP情况:
X-Forwarded-For中包含多个IP(如168.1.1, 10.0.0.1),我们使用split(",")[0]来获取最左边的、最原始的客户端IP。 - 处理本地IP:
getRemoteAddr()在本地访问时可能返回0:0:0:0:0:0:0:1(IPv6的localhost),我们将其统一转换为0.0.1。
生产环境配置:以Nginx为例
仅仅有Java代码是不够的,服务器端的配置同样关键,如果你使用Nginx作为反向代理,你需要确保Nginx正确地设置了 X-Forwarded-For 头。
Nginx 配置示例 (nginx.conf)
server {
listen 80;
server_name yourdomain.com;
# 将所有请求转发到后端的Tomcat/Apache
location / {
proxy_pass http://localhost:8080; # 后端应用地址
# 关键配置:将客户端真实IP传递给后端
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# 其他代理设置...
proxy_connect_timeout 60s;
}
}
配置解析:
proxy_set_header X-Real-IP $remote_addr;:将Nginx直接收到的客户端IP($remote_addr)设置为X-Real-IP头。proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;:这是最关键的配置。$proxy_add_x_forwarded_for会将Nginx收到的X-Forwarded-For头(如果有的话)与$remote_addr(客户端IP)用逗号连接起来,形成一个完整的IP链,然后传递给后端,这样后端就能看到完整的代理路径。
总结与最佳实践
- 没有银弹: 不存在一个“放之四海而皆准”的IP获取方法,最佳实践是结合 Java代码逻辑 和 服务器端配置。
- 信任链: 你的Java代码应该信任你的代理服务器(如Nginx、SLB)传递过来的头信息,因为它们处于你的信任域内,但对于来自公网、不可信的请求头,要保持警惕。
- 工具化: 将IP获取逻辑封装成一个独立的工具类(如上面的
IpUtil),在项目中统一调用,避免重复代码和不一致的处理方式。 - 日志记录: 在记录用户IP时,可以考虑同时记录
getRemoteAddr()和所有相关的请求头信息,便于在出现问题时进行排查。 - IP校验: 在进行任何基于IP的业务逻辑(如风控)前,最好对IP地址进行格式校验,确保其有效性。
获取客户端真实IP是Java Web开发中的一个基础但至关重要的环节,理解其背后的原理——特别是代理服务器如何通过HTTP头传递信息——是解决问题的关键,希望本文提供的从基础到实战的完整指南,能帮助你彻底掌握这项技能,从容应对各种复杂的网络环境。
你觉得这篇文章对你有帮助吗?欢迎在评论区分享你在获取IP时遇到的奇葩问题或独到见解!
