Nginx 反向代理后,Java 服务端为什么拿不到真实客户端 IP?
Nginx 反向代理后,Java 服务端为什么拿不到真实客户端 IP?
一次操作日志全是服务器 IP 的排查记录
问题现象
线上环境通过 Nginx 反向代理访问 Java 后端服务,操作日志里记录的客户端 IP 永远是服务器公网 IP,而不是真实用户的 IP。诡异的是,Nginx 的 access.log 里能正常看到客户端真实 IP。
排查过程
先看服务端获取 IP 的代码,典型写法:
public static String getLocalIp(HttpServletRequest request) {
String remoteAddr = getIpAddr(request);
String forwarded = request.getHeader("X-Forwarded-For");
String realIp = request.getHeader("X-Real-IP");
String ip = null;
if (realIp == null) {
if (forwarded == null) {
ip = remoteAddr;
} else {
ip = remoteAddr + "/" + forwarded.split(",")[0];
}
} else {
if (realIp.equals(forwarded)) {
ip = realIp;
} else {
if (forwarded != null) {
forwarded = forwarded.split(",")[0];
}
ip = realIp + "/" + forwarded;
}
}
return ip;
}
private static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
代码逻辑没问题,先取 X-Real-IP,再取 X-Forwarded-For,最后 fallback 到 getRemoteAddr()。既然拿到的永远是服务器 IP,说明前面两个 Header 都是 null,走了最后的 fallback。
那问题就很明确了——Nginx 没有把客户端 IP 通过 Header 传给后端。
定位 Nginx 配置
看 Nginx 配置中反向代理的部分:
location /jshERP-boot/ {
proxy_pass http://www.xnysj.cn:9999/jshERP-boot/;
proxy_set_header Host $host;
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 60s;
}
问题就在这里——只设置了 Host,没有设置 X-Real-IP 和 X-Forwarded-For。Nginx 本身知道客户端 IP(所以 access.log 正常),但它没把这个信息传给后端,后端只能拿到 getRemoteAddr() 返回的 Nginx 服务器 IP。
修复
补两行 proxy_set_header 就行:
location /jshERP-boot/ {
proxy_pass http://www.xnysj.cn:9999/jshERP-boot/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; # 新增
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 新增
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 60s;
}
改完执行 nginx -t && nginx -s reload,操作日志就能拿到真实客户端 IP 了。
延伸:这两个 Header 的区别
| Header | 值 | 说明 |
|---|---|---|
X-Real-IP |
$remote_addr |
直接连接 Nginx 的客户端 IP,永远是单个 IP |
X-Forwarded-For |
$proxy_add_x_forwarded_for |
追加式,经过多层代理时会累积成 客户端IP, 代理1, 代理2 |
一般场景下用 X-Real-IP 就够了,它最直接。X-Forwarded-For 适合多层代理链的场景。
代码简化建议
既然 Nginx 已经设置了 X-Real-IP,服务端的 getIpAddr 方法返回值就是真实客户端 IP,getLocalIp 里那些复杂的拼接逻辑其实可以简化:
public static String getLocalIp(HttpServletRequest request) {
return getIpAddr(request);
}
X-Real-IP 就是 $remote_addr,是客户端真实 IP,不需要再和 X-Forwarded-For 拼来拼去。
总结
| 问题 | 原因 | 修复 |
|---|---|---|
| 操作日志全是服务器 IP | Nginx 没传 X-Real-IP / X-Forwarded-For |
加两行 proxy_set_header |
排查这类问题的通用思路:先确认服务端收到的 Header 值(打个日志或 debug),如果都是 null 就往前查 Nginx 配置,如果 Header 有值但不对就查多层代理链路。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)