如何在 Nginx 反向代理下向后端服务透传真实用户 IP
在 Nginx 作为反向代理前置的场景下,要让后端服务或安全策略获取真实用户 IP,标准做法是通过 HTTP Header 透传,并在后端应用中解析该字段。需要特别说明的是:网络层防火墙(如 iptables、firewalld)工作在网络层,无法读取 HTTP 协议头中的 IP 信息,因此无法直接配置防火墙来实现此功能。必须在应用层通过 Header 传递,并由后端安全模块或代码读取。
先说结论:此方案适合 Nginx 反向代理架构,需先确认后端应用支持解析自定义 Header,最后通过日志验收 IP 字段是否正确。
- 适合:Nginx 作为唯一入口流量的反向代理环境
- 先准备:后端应用程序代码或安全模块需配置信任该 Header
- 验收:检查后端访问日志中记录的客户端 IP 是否为公网 IP
- 注意:网络防火墙无法识别 X-Forwarded-For,请勿在 iptables 中尝试匹配
原理说明:为何防火墙无法实现
当 Nginx 作为反向代理时,后端服务器看到的 TCP 连接来源是 Nginx 服务器的内网 IP,而不是用户的公网 IP。这是因为 TCP 连接在 Nginx 处终止,Nginx 再与后端建立新的连接。普通的网络防火墙(如 iptables)工作在网络层,无法读取 HTTP 协议头中的信息。若需在网络层限制 IP,只能基于 Nginx 的 IP 进行;若需基于用户真实 IP 限制,必须使用应用层 WAF 或在后端代码中实现。
Nginx 配置示例
以下是完整的 Nginx server 配置块示例。请根据实际域名和后端地址修改。
server {
listen 80;
server_name example.com;
location / {
# 后端服务地址
proxy_pass http://127.0.0.1:8080;
# 透传真实用户 IP
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_set_header X-Forwarded-Proto $scheme;
# 可选:设置超时时间
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}修改后执行以下命令检查语法并生效:
nginx -t
nginx -s reload后端应用适配代码
后端服务默认可能不信任 X-Forwarded-For,需在代码或配置中显式获取该字段。
1. PHP 示例
<?php
// 优先读取 X-Forwarded-For,其次读取 REMOTE_ADDR
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
// 如果有多个 IP(经过多层代理),取第一个
if (strpos($ip, ',') !== false) {
$ip = trim(explode(',', $ip)[0]);
}
echo "Client IP: " . $ip;
?>2. Java Spring Boot 示例
在 application.properties 中配置:
server.use-forward-headers=true
server.tomcat.remoteip.internal-proxies=10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}代码中获取:
@GetMapping("/ip")
public String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}3. Python Flask 示例
from flask import request
@app.route('/ip')
def get_ip():
if request.headers.getlist("X-Forwarded-For"):
ip = request.headers.getlist("X-Forwarded-For")[0]
else:
ip = request.remote_addr
return ip怎么验证是否生效
1. 查看后端日志
访问网站后,检查后端应用的访问日志。如果配置成功,日志中记录的客户端 IP 应为用户的公网 IP,而不是 Nginx 的内网 IP。
2. 使用 curl 测试
在后端部署一个简单的显示 IP 的接口,通过 Nginx 访问:
curl -H "Host: example.com" http://nginx_server_ip/ip观察返回内容是否为您的本地公网 IP。
安全策略与常见坑
1. 直接访问后端 bypass Nginx
如果后端服务器端口未对公网防火墙隐藏,用户可能直接访问后端 IP。此时后端收到的请求没有经过 Nginx,Header 为空或被伪造,导致 IP 记录错误或安全策略失效。建议:在云安全组或硬件防火墙中,仅允许 Nginx 服务器 IP 访问后端端口(如 8080)。
2. 盲目信任 Header 导致 IP 伪造
如果 Nginx 不是唯一入口,或者攻击者能直接向后端发送请求,他们可以伪造
X-Forwarded-For。务必确保后端只信任来自 Nginx 服务器的请求中的该 Header。在 Spring Boot 等框架中,需配置trusted-proxies。3. 网络防火墙无法识别
不要尝试在 iptables 或 firewalld 中直接基于
X-Forwarded-For做放行或拦截,网络层防火墙无法解析 HTTP 内容。此类需求需使用应用层 WAF(如 ModSecurity、云 WAF)或在前端 Nginx 层通过limit_req做限制。参考来源
- Nginx 官方文档 - ngx_http_proxy_module: proxy_set_header
- Nginx 官方文档 - ngx_http_realip_module (相关概念参考): ngx_http_realip_module