Nginx `$remoteaddr` 详解与应用 – wiki大全

Nginx $remote_addr 详解与应用

在 Nginx 中,$remote_addr 是一个核心的内置变量,它记录了与 Nginx 服务器建立 TCP 连接的客户端的 IP 地址。理解其工作原理及其在不同部署环境下的行为,对于 Nginx 的日志记录、访问控制、安全审计以及后端应用获取真实用户 IP 至关重要。

$remote_addr 的基本概念

  • 直接连接场景: 当客户端直接连接到 Nginx 服务器时,$remote_addr 将准确地反映客户端的真实 IP 地址。这种情况下,$remote_addr 的值就是请求的发起者。

  • 代理连接场景: 然而,在现代复杂的网络架构中,Nginx 往往不是直接面向用户的唯一服务器。它可能部署在 CDN、负载均衡器、WAF(Web Application Firewall)或其他反向代理服务器之后。在这种情况下,$remote_addr 记录的将是直接与当前 Nginx 实例建立连接的上一级代理服务器的 IP 地址,而不是原始客户端的真实 IP。

为什么需要获取真实客户端 IP?

当 Nginx 处于代理链中时,如果直接依赖 $remote_addr,会导致以下问题:

  1. 日志记录失真: 访问日志中记录的都是代理服务器的 IP,无法准确追踪用户行为。
  2. 访问控制失效: 基于 IP 的黑白名单、限速等策略会错误地作用于代理服务器,而非真实用户。
  3. 统计分析偏差: 用户地域分布、活跃用户统计等数据将不准确。
  4. 安全审计困难: 难以追溯恶意攻击的真实来源。
  5. 后端应用获取 IP 错误: 后端服务(如 PHP、Java、Node.js 应用)通过 Nginx 获取到的 IP 也是代理 IP,影响其业务逻辑。

解决方案:ngx_http_realip_module 模块

为了解决上述问题,Nginx 提供了 ngx_http_realip_module 模块,它允许 Nginx 从特定的 HTTP 请求头中提取客户端的真实 IP 地址,并将其重新赋值给 $remote_addr 变量。

1. 模块的编译与检查

ngx_http_realip_module 并非 Nginx 默认编译模块,可能需要手动启用。
你可以通过以下命令检查 Nginx 是否已包含该模块:

bash
nginx -V

在输出的编译参数中查找 --with-http_realip_module。如果不存在,则需要重新编译 Nginx,并在 configure 命令中添加此参数。

2. 配置 ngx_http_realip_module

在 Nginx 的配置文件(通常在 http 块、server 块或 location 块中)中,使用以下指令进行配置:

“`nginx
http {
# … 其他配置 …

# 信任的代理服务器IP地址或CIDR范围。
# Nginx只会从这些IP地址发来的请求中,根据real_ip_header提取真实IP。
# 来自非信任IP的请求,$remote_addr将保持不变。
set_real_ip_from 192.168.1.0/24;  # 示例:一个私有网络段
set_real_ip_from 10.0.0.1;        # 示例:一个具体的代理服务器IP
set_real_ip_from 172.16.0.0/12;   # 示例:另一个私有网络段,常见于云服务商负载均衡

# 指定从哪个HTTP头中获取真实IP。
# 最常用的是 'X-Forwarded-For' 和 'X-Real-IP'。
# 'X-Forwarded-For' 通常用于多级代理,其中包含了客户端IP以及中间代理的IP链。
# 'X-Real-IP' 通常由单个代理设置。
real_ip_header X-Forwarded-For;

# 当存在多级代理时,此指令控制如何解析real_ip_header。
# on: Nginx会从real_ip_header中从右向左遍历IP地址,直到找到第一个非信任的IP地址,
#     并将其作为客户端真实IP。这对于确保获取最原始的客户端IP非常关键。
# off: Nginx只取real_ip_header中最左侧的IP。
real_ip_recursive on;

server {
    listen 80;
    server_name example.com;

    location / {
        # 在此location中,$remote_addr 已经被修正为真实客户端IP。
        # 可以在日志中使用修正后的 $remote_addr。
        log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"'; # 也可以同时记录XFF头
        access_log /var/log/nginx/access.log main;

        # ... 其他配置 ...
    }
}

}
“`

指令详解:

  • set_real_ip_from address | CIDR: 定义受信任的代理服务器的 IP 地址或 IP 段。只有来自这些 IP 的请求,Nginx 才会尝试从 real_ip_header 中提取真实 IP。
  • real_ip_header field: 指定 Nginx 应该检查哪个 HTTP 请求头来获取真实的客户端 IP。常用的有 X-Forwarded-For (XFF) 和 X-Real-IP
    • X-Forwarded-For 头格式通常为 client_ip, proxy1_ip, proxy2_ip, ...
    • X-Real-IP 通常只包含一个 IP 地址。
  • real_ip_recursive on | off: 当设置为 on 时,如果 real_ip_header (如 X-Forwarded-For) 包含多个 IP,Nginx 会从右向左查找第一个不在 set_real_ip_from 列表中的 IP 地址,并将其识别为客户端的真实 IP。这对于正确处理多级代理链至关重要。

将真实 IP 传递给后端服务器

即使 Nginx 自身获取到了真实 IP,如果 Nginx 作为反向代理将请求转发给后端应用服务器,也需要将这个真实 IP 传递下去,以便后端应用也能正确获取。这通常通过 proxy_set_header 指令实现:

“`nginx
server {
listen 80;
server_name your_domain.com;

location / {
    proxy_pass http://backend_servers;

    # 将 Nginx 修正后的 $remote_addr 传递给后端,通常使用 X-Real-IP 头。
    # 后端应用可以通过读取这个头来获取客户端真实IP。
    proxy_set_header X-Real-IP $remote_addr;

    # 维护 X-Forwarded-For 链。
    # $proxy_add_x_forwarded_for 是一个特殊的 Nginx 变量,
    # 它会在当前请求的 X-Forwarded-For 值后面追加 Nginx 的 $remote_addr (即上一级代理IP或真实客户端IP)。
    # 如果原始请求没有 X-Forwarded-For 头,它会直接使用 $remote_addr。
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 传递 Host 头,确保后端正确处理域名。
    proxy_set_header Host $host;
}

}
“`

相关变量与指令:

  • $remote_addr: 经过 ngx_http_realip_module 处理后,此变量将包含客户端的真实 IP。
  • $http_x_forwarded_for: Nginx 接收到的原始请求中的 X-Forwarded-For HTTP 头的值。
  • $proxy_add_x_forwarded_for: 这是一个非常有用的 Nginx 变量。它会自动将当前 $remote_addr 的值添加到 $http_x_forwarded_for 头部(如果存在,则追加并用逗号分隔;如果不存在,则直接使用 $remote_addr 的值)。这确保了在代理链中,每个代理都能将其上游的 IP 信息添加到 X-Forwarded-For 链中,使得后端应用能获得完整的代理路径。

总结

$remote_addr 是 Nginx 中识别客户端 IP 的关键变量。在部署有代理的环境中,为了获取客户端的真实 IP 地址,必须正确配置 ngx_http_realip_module 模块。这包括:

  1. 确认模块已编译: nginx -V 检查 --with-http_realip_module
  2. 定义信任源: 使用 set_real_ip_from 指定所有代理服务器的 IP 地址或 CIDR 范围。
  3. 指定头部: 使用 real_ip_header 指明从哪个 HTTP 头(通常是 X-Forwarded-ForX-Real-IP)获取真实 IP。
  4. 处理多级代理: 针对多级代理链,开启 real_ip_recursive on 以正确解析最原始的客户端 IP。
  5. 转发给后端: 作为反向代理时,使用 proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 将真实 IP 信息传递给后端应用。

通过上述配置,可以确保 Nginx 及其后端服务能够准确地获取、记录和使用客户端的真实 IP 地址,从而支持正确的日志分析、访问控制和业务逻辑。

滚动至顶部