Nginx获取客户端真实IP

一、引言:为什么 $remote_addr 不再是"真实"的?

在经典的单机 Web 架构中,应用服务器可以直接通过 REMOTE_ADDR 获取到客户端的真实公网 IP。然而,在现代分布式架构中,我们的服务前面通常会部署 CDN、云负载均衡器(如 AWS ALB, 腾讯云 CLB)或 Nginx 反向代理集群

在这种多层代理的环境下,当请求最终到达你的应用服务器时:

  • TCP 连接的源 IP ($remote_addr) 已经变成了上一级代理服务器的内网 IP
  • 客户端的真实 IP 就像被层层包裹的信件,如果不进行特殊处理,你的应用将永远无法知晓它。

💡 核心价值

准确获取客户端真实 IP,是实现访问控制、安全审计、地域化服务、个性化推荐等几乎所有业务功能的前提

本文将带你彻底搞懂 Nginx 在不同场景下透传和获取真实 IP 的完整方案。


二、两大核心机制:Header 透传 vs. 内置变量替换

要解决这个问题,主要有两种思路:

思路一:Header 透传(最常用)

  • 原理 :每一层代理服务器都将客户端的 IP 地址(或整个代理链路)写入 HTTP 请求头(如 X-Forwarded-For, X-Real-IP),并传递给后端。
  • 优点:简单、通用,适用于任何支持读取 HTTP Header 的后端应用(Java, Python, Go, PHP 等)。
  • 缺点:HTTP Header 可以被客户端伪造,存在安全风险。

思路二:内置变量替换(更安全)

  • 原理 :利用 Nginx 的 ngx_http_realip_module 模块,直接将 $remote_addr 变量的值替换为从可信 Header 中提取的真实 IP。
  • 优点:对后端应用完全透明,无需修改代码;并且只信任来自特定 IP 段的 Header,安全性更高。
  • 缺点 :需要 Nginx 编译时包含 http_realip_module(大多数发行版默认已包含)。

三、实战详解:Header 透传方案

这是最基础也是最广泛使用的方法。

1. 核心 Header 介绍

  • X-Forwarded-For (XFF)
    • 格式 : X-Forwarded-For: client_ip, proxy1_ip, proxy2_ip
    • 作用 : 记录了从客户端到服务器之间经过的所有代理 IP 列表。最左边的是原始客户端 IP。
    • 特点: 是一个标准的、可追加的列表。每一层代理都会将自己的 IP 追加到末尾。
  • X-Real-IP
    • 格式 : X-Real-IP: client_ip
    • 作用 : 通常只存放最原始的客户端 IP
    • 特点: 是一个简单的键值对,由第一层代理设置,后续代理通常不再修改。

2. Nginx 配置示例

复制代码
# 假设这是你的边缘 Nginx(直接面向公网)
server {
    listen 80;
    server_name example.com;

    location / {
        # 将客户端的真实IP(即当前的$remote_addr)放入X-Real-IP
        proxy_set_header X-Real-IP $remote_addr;
        
        # 处理X-Forwarded-For
        # 如果请求中已有XFF,则追加当前代理IP;否则,创建一个新的XFF并填入客户端IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        proxy_pass http://backend_app;
    }
}
  • $proxy_add_x_forwarded_for : 这是一个 Nginx 内置变量,它的值等于 X-Forwarded-For 请求头的值加上 ", "$remote_addr。如果原请求没有 X-Forwarded-For 头,它的值就是 $remote_addr

3. 后端应用如何获取?

后端应用需要主动从 HTTP Header 中读取这些值。

  • Python (Django/Flask): request.META.get('HTTP_X_REAL_IP')request.headers.get('X-Forwarded-For').split(',')[0]
  • Java (Spring Boot): @RequestHeader("X-Real-IP") String realIp
  • Node.js (Express): req.headers['x-real-ip']

⚠️ 安全警告 :由于 Header 可伪造,切勿 在边缘 Nginx 上直接信任客户端传来的 X-Forwarded-For。正确的做法是,只有你的内部可信代理才能设置或追加这个 Header。


四、高级方案:使用 real_ip 模块(推荐用于内部服务)

当你有多个 Nginx 层级,或者希望对后端应用完全透明时,real_ip 模块是更好的选择。

1. 工作原理

real_ip 模块允许你指定:

  • 哪些上游 IP 是可信的(即你的 CDN 或 LB 的回源 IP)。
  • 从哪个 Header 中提取真实 IP

当请求来自可信 IP 时,Nginx 会用 Header 中的 IP 覆盖 $remote_addr 的值。这样,对于后端 Nginx 或应用来说,$remote_addr 就已经是真实 IP 了。

2. Nginx 配置示例

复制代码
# 假设这是你的内部 Nginx(接收来自公网 Nginx 或云LB的流量)
http {
    # 定义可信的代理IP段
    # 例如,阿里云SLB的经典网络回源IP段
    set_real_ip_from 100.64.0.0/10;
    # 例如,你的公网Nginx集群的内网IP段
    set_real_ip_from 192.168.1.0/24;
    
    # 指定从哪个Header中获取真实IP
    real_ip_header X-Forwarded-For;
    # 或者
    # real_ip_header X-Real-IP;
    
    # 开启递归解析(针对X-Forwarded-For)
    # 会从右向左扫描XFF列表,直到找到第一个不在可信IP列表中的IP作为真实IP
    real_ip_recursive on;

    server {
        listen 8080;
        location / {
            # 此时,$remote_addr 已经是客户端的真实IP了!
            # 你可以直接用它做日志记录、限流等操作
            access_log /var/log/nginx/access.log main;
            
            # 如果后面还有应用,也可以继续透传
            proxy_set_header X-Real-IP $remote_addr;
            proxy_pass http://app_server;
        }
    }
}
  • set_real_ip_from: 必须配置,用于声明信任的上游代理。
  • real_ip_recursive on : 对于 X-Forwarded-For 至关重要。它能智能地从 client, proxy1, proxy2 这样的列表中,剔除掉所有可信的代理 IP,从而精准地找到最原始的客户端 IP。

3. 日志记录

配置好 real_ip 模块后,你的 Nginx access_log 中记录的 $remote_addr 就是真实 IP,无需任何额外处理。

复制代码
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent"';

access_log /var/log/nginx/access.log main; # $remote_addr 即真实IP

五、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
甲维斯2 小时前
GLM5.2超过Opus4.8Think,全球第二了!
前端·人工智能·ai编程
by————组态2 小时前
Ricon组态系统 - 新一代Web可视化组态平台
前端·后端·物联网·架构·组态·组态软件
JieE2122 小时前
手把手带你用纯 CSS 实现一个 3D 旋转魔方,这些前端基础你能打几分?
前端·css·html
lichenyang4532 小时前
鸿蒙 Web 容器(二):H5 和 ArkTS 说话前,先定一份「协议」
前端
JYeontu2 小时前
开箱流水加载动画
前端·javascript·css
RANxy2 小时前
AntV 入门系列:G6 图可视化实战
前端
尽欢i2 小时前
Vue3 customRef 封神教程:防抖、本地存储、自动埋点一套搞定,模板干干净净
前端·javascript·vue.js
VOLUN2 小时前
TypeScript封装通用RESTful BaseAPI,后台接口代码精简80%
前端·javascript