如何在 Nginx 前置环境下配置防火墙透传真实用户 IP

文章导读
在 Nginx 作为反向代理前置的场景下,要让后端服务或安全策略获取真实用户 IP,标准做法是通过 HTTP Header 透传,并在后端应用中解析该字段。需要特别说明的是:网络层防火墙(如 iptables、firewalld)工作在网络层,无法读取 HTTP 协议头中的 IP 信息,因此无法直接配置防火墙来实现此功能。必须在应用层通过 Header 传递,并由后端安全模块或代码读取。
📋 目录
  1. A 原理说明:为何防火墙无法实现
  2. B Nginx 配置示例
  3. C 后端应用适配代码
  4. D 怎么验证是否生效
  5. E 安全策略与常见坑
  6. F 参考来源
A A

如何在 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 示例

如何在 Nginx 前置环境下配置防火墙透传真实用户 IP
<?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. 查看后端日志

如何在 Nginx 前置环境下配置防火墙透传真实用户 IP

访问网站后,检查后端应用的访问日志。如果配置成功,日志中记录的客户端 IP 应为用户的公网 IP,而不是 Nginx 的内网 IP。

2. 使用 curl 测试

在后端部署一个简单的显示 IP 的接口,通过 Nginx 访问:

curl -H "Host: example.com" http://nginx_server_ip/ip

观察返回内容是否为您的本地公网 IP。

安全策略与常见坑

1. 直接访问后端 bypass Nginx

如何在 Nginx 前置环境下配置防火墙透传真实用户 IP

如果后端服务器端口未对公网防火墙隐藏,用户可能直接访问后端 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 做限制。

参考来源