参考链接:详解 Nginx 获取客户端真实 IP
1. 概述
1.1 为什么需要获取客户端 IP
现实生产环境中,客户端访问服务通常是经过了好多层 cdn、waf、负载均衡、代理等才能最终到达服务的。但通常如果是没有经过配置的代理,是无法将客户端真实 IP 地址透传给应用的。应用如果没法获取到真实的客户端 IP 地址,就无法判断客户端访问是否合法,也无法针对对应的客户提供对应的服务,所以,获取客户端 IP 地址是真正需要的。
1.2 nginx 配置中获取客户端 IP 的方式
nginx 配置中可以通过如下几种方式获取客户端 IP;这里说的获取客户端 IP,是通过 nginx 配置,将对应参数中的值赋给 $remote_addr
以下是一些可以用来传递客户端真实 IP 的请求头
1.3 连接来源 IP vs 真实客户端 IP
1.3.1 连接来源 IP
定义:与 Nginx 建立 TCP 连接的设备 IP,即上一层代理的 IP 地址
获取方式:通过系统调用
getpeername() 获得特点:无法被应用层伪造,是真实的网络层信息
在 Nginx 中:对应
$realip_remote_addr变量
1.3.2 真实客户端 IP
定义:最终用户的实际 IP 地址
获取方式:从 HTTP 头部解析
特点:可能被中间代理修改或伪造
在 Nginx 中:经过处理后的
$remote_addr 变量
1.4 Nginx 相关参数配置
1.4.1 real_ip_header
作用:指定从哪个 HTTP 头部获取真实 IP
# 常用配置
real_ip_header X-Forwarded-For; # 标准配置
real_ip_header X-Real-IP; # 简单代理
real_ip_header CF-Connecting-IP; # Cloudflare CDN
1.4.2 set_real_ip_from
作用:定义可信任的代理 IP 范围(白名单机制)
# 基本语法
set_real_ip_from ip_address;
set_real_ip_from ip_address/netmask;
set_real_ip_from unix:;
# 实际示例
set_real_ip_from 10.0.0.0/8; # 内网段
set_real_ip_from 172.16.0.0/12; # 私有网络
set_real_ip_from 192.168.1.100; # 特定 IP
1.4.3 real_ip_recursive
作用:启用递归处理多层代理
real_ip_recursive on; # 启用(推荐)
real_ip_recursive off; # 禁用(默认)
1.5 工作机制详解
1.5.1 白名单验证机制
set_real_ip_from 采用白名单机制:
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
1.5.1.1 工作流程
检查连接来源 IP
如果来源 IP 在白名单中 → 信任请求 → 解析 HTTP 头部
如果来源 IP 不在白名单中 → 不信任请求 → 忽略 HTTP 头部
1.5.1.2 示例对比
来源 IP 在白名单中:
# 连接来源: 10.0.1.100 (在 10.0.0.0/8 范围内)
X-Forwarded-For: 1.2.3.4
# 结果: $remote_addr = 1.2.3.4
来源 IP 不在白名单中:
# 连接来源: 8.8.8.8 (✗ 不在白名单范围内)
X-Forwarded-For: 1.2.3.4
# 结果: $remote_addr = 8.8.8.8 (忽略头部信息)
1.5.2 多值 X-Forwarded-For 处理算法
1.5.2.1 X-Forwarded-For 格式
X-Forwarded-For: client_ip, proxy1_ip, proxy2_ip, proxy3_ip
←────────── 从左到右:客户端到服务器 ──────────→
1.5.2.2 非递归模式 real_ip_recursive off
选择 最后一个(最右边) IP:
set_real_ip_from 192.168.1.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive off;
# X-Forwarded-For: 1.2.3.4, 172.16.1.10, 192.168.1.50
# 选择: 192.168.1.50
1.5.2.3 递归模式 real_ip_recursive on
从右到左检查,跳过可信 IP,选择第一个不可信 IP:
set_real_ip_from 192.168.1.0/24; # 可信代理1
set_real_ip_from 172.16.1.0/24; # 可信代理2
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# X-Forwarded-For: 1.2.3.4, 172.16.1.10, 192.168.1.50
# 处理过程:
# 1. 检查 192.168.1.50 → 可信,跳过
# 2. 检查 172.16.1.10 → 可信,跳过
# 3. 检查 1.2.3.4 → 不可信,选择
# 结果: $remote_addr = 1.2.3.4
1.5.2.4 不同匹配程度的处理逻辑
假设 X-Forwarded-For: 1.2.3.4, 203.0.113.5, 172.16.1.10,连接来源为 172.16.1.10:
情况 1:只匹配最后一个
set_real_ip_from 172.16.0.0/12; # 只信任 172.16.1.10
# 处理: 172.16.1.10(跳过) → 203.0.113.5(选择)
# 结果: $remote_addr = 203.0.113.5
情况 2:匹配最后两个
set_real_ip_from 172.16.0.0/12; # 信任 172.16.1.10
set_real_ip_from 203.0.113.0/24; # 信任 203.0.113.5
# 处理: 172.16.1.10(跳过) → 203.0.113.5(跳过) → 1.2.3.4(选择)
# 结果: $remote_addr = 1.2.3.4
情况 3:全部匹配
set_real_ip_from 172.16.0.0/12; # 信任 172.16.1.10
set_real_ip_from 203.0.113.0/24; # 信任 203.0.113.5
set_real_ip_from 1.2.3.0/24; # 信任 1.2.3.4
# 处理: 所有IP都可信 → 选择最左边的IP
# 结果: $remote_addr = 1.2.3.4
除了上述情况,还有其他情况,比如:
情况 4:连接来源不可信,也就是没有任何一个 IP 在可信列表中
set_real_ip_from 172.16.0.0/12; # 信任 172.16.1.10
# 但连接来源是: 8.8.8.8 (不在可信列表中)
# X-Forwarded-For: 1.2.3.4, 203.0.113.5, 172.16.1.10
# 处理: 连接来源不可信 → 完全忽略 X-Forwarded-For 头部
# 结果: $remote_addr = 8.8.8.8
2. 实验
实验拓扑如下:

2.1 基础环境
这里主要讨论的是 server1 与 server2 服务中的 nginx 配置,主要的 nginx配置如下
proxy 配置:
map $http_upgrade $connection_upgrade { default upgrade; '' close; } log_format proxy '$remote_addr - $remote_user [$time_local] "$request" ' ' http_x_forwarded_for: "$http_x_forwarded_for"' ' x-real-ip:"$http_x_real_ip"'; server { listen 80; sendfile on; include mime.types; keepalive_timeout 65; client_max_body_size 500M; client_body_buffer_size 128k; proxy_connect_timeout 600; proxy_read_timeout 600; proxy_send_timeout 600; index index.html index.htm index.php; #error_page 404 /404.html; location ~ /\. { deny all; } access_log /var/log/nginx/jenkins_proxy.access.log proxy; error_log /var/log/nginx//jenkins_proxy.error.log; location / { proxy_redirect off; add_header 'Access-Control-Allow-Origin' "http://192.168.2.51" always; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Accept-Encoding ""; proxy_set_header Accept-Language "zh-CN"; proxy_pass http://server; # 这里地址不同 } }
server 的完整配置如下:
worker_processes 1; worker_priority 0; worker_cpu_affinity auto; error_log /dev/fd/2; pid /run/nginx/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; server_tokens off; keepalive_timeout 65; client_max_body_size 100M; client_body_buffer_size 102400k; proxy_connect_timeout 600; proxy_read_timeout 600; proxy_send_timeout 600; proxy_hide_header Server; client_header_buffer_size 4k; large_client_header_buffers 4 16k; client_body_temp_path /tmp/client_body; fastcgi_temp_path /tmp/fastcgi_temp; proxy_temp_path /tmp/proxy_temp; scgi_temp_path /tmp/scgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; gzip on; gzip_comp_level 1; gzip_buffers 32 16k; gzip_min_length 20; gzip_static on; gzip_types text/css text/javascript text/xml text/html text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/font-woff application/vnd.ms-fontobject image/svg+xml; # 设置日志格式,打印参数 # $http_x_forwarded_for 请求头x_forwarded_for的值 # $remote_addr 匹配后得到的客户端IP # $realip_remote_addr 上面提到过的连接来源IP log_format netdata '$real_ip - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '$request_length $request_time $upstream_response_time $upstream_addr ' 'httpx_forwarded_for:"$http_x_forwarded_for" - ' 'remote_addr:"$remote_addr" - realip_remote_addr:"$realip_remote_addr"'; map $http_x_real_ip $real_ip { default $http_x_real_ip; "" $remote_addr; } map $http_x_scheme $real_http_x_scheme { default $http_x_scheme; "" $scheme; } map $http_x_original_request_uri $original_request_uri { default $http_x_original_request_uri; "" $real_http_x_scheme://$http_host$request_uri; } map $sent_http_content_type $cache_control_type { default "private"; ~text/html "no-store, no-transform"; } map $http_upgrade $connection_upgrade { default upgrade; '' close; } # 核心配置: set_real_ip_from 172.0.1.0/24; set_real_ip_from 172.0.100.0/24; set_real_ip_from 10.0.1.0/24; set_real_ip_from 192.168.2.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on; server { listen 80; server_name _; access_log /dev/fd/1 netdata; error_log /dev/fd/2 error; location / { gzip_static on; root /app/frontend; index index.html; try_files $uri $uri/ /index.html; add_header Cache-Control $cache_control_type; open_file_cache max=64 inactive=30d; open_file_cache_min_uses 8; open_file_cache_valid 5s; } location ~ ^/api/ { proxy_request_buffering off; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $real_ip; proxy_set_header X-Scheme $real_http_x_scheme; proxy_set_header X-Original-Request-URI $original_request_uri; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_pass http://127.0.0.1:8080; } } }
2.2 实验整体过程
2.2.1 测试白名单验证机制
设置来源 IP 不可信
只添加如下配置,设置 ip 头为
X-Forwarded-For 访问real_ip_header X-Forwarded-For;查看日志:
172.0.100.1 - - [18/Sep/2025:11:49:38 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.1.9" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:11:49:38 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.002 0.002 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.1.9" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:11:49:38 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.1.9" - realip_remote_addr:"172.0.1.9"可以看出,当没有设置可信 ip 时,会忽略请求头
X-Forwarded-For 的值,默认取 连接来源 IP 为remote_addr
设置来源 IP 可信
添加如下配置,添加来源 IP 可信
set_real_ip_from 172.0.1.0/24; real_ip_header X-Forwarded-For;查看日志:
172.0.100.1 - - [18/Sep/2025:12:01:04 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:01:04 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.001 0.001 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:01:04 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"可以看到,默认情况下,由于我们设置了来源 IP 可信,这时会启用
X-Forwarded-For中的值来进行判断并设置remote_addr 的值,多值X-Forwarded-For的实验,我们放到下一节来进行讨论。
2.2.2 测试多值 X-Forwarded-For
2.2.2.1 非递归模式 real_ip_recursive off (默认)
如果不进行设置默认会选取非递归模式,即直接选取最后一个不可信的 IP 作为 remote_addr 的值,上面实验就是这种情况,这里我们再测试一下设置最后一个 IP 可信,再看看结果会是什么样
set_real_ip_from 172.0.1.0/24;
set_real_ip_from 172.0.100.0/24;
real_ip_header X-Forwarded-For;
查看日志:
172.0.100.1 - - [18/Sep/2025:12:11:37 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"
172.0.100.1 - - [18/Sep/2025:12:11:37 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.002 0.001 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"
172.0.100.1 - - [18/Sep/2025:12:11:38 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"
可以看到,我们设置了最后一个 IP 可信时,最后取到的 remote_addr 值都为 X-Forwarded-For 的最后一个 IP,可以再测试一下将所有 IP 设置为可信
set_real_ip_from 172.0.1.0/24;
set_real_ip_from 172.0.100.0/24;
set_real_ip_from 10.0.1.0/24;
set_real_ip_from 192.168.2.0/24;
real_ip_header X-Forwarded-For;
查看日志:
172.0.100.1 - - [18/Sep/2025:12:15:20 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"
172.0.100.1 - - [18/Sep/2025:12:15:20 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.002 0.001 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"
172.0.100.1 - - [18/Sep/2025:12:15:21 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"
可以看出,当 非递归模式 且 来源 IP 配置为可信 时,无论 X-Forwarded-For 的值是否配置为可信,都取其最后一个值为 remote_addr
2.2.2.2 递归模式 real_ip_recursive on
首先测试只开递归模式,并设置来源 IP 可信,配置如下:
set_real_ip_from 172.0.1.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on;查看日志:
172.0.100.1 - - [18/Sep/2025:12:21:03 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:21:03 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.002 0.001 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:21:04 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"172.0.100.1" - realip_remote_addr:"172.0.1.9"可以看到这里取了最后一个值为
remote_addr接着测试将最后一个值设置为可信,配置如下:
set_real_ip_from 172.0.1.0/24; set_real_ip_from 172.0.100.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on;查看日志:
172.0.100.1 - - [18/Sep/2025:12:23:22 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"10.0.1.1" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:23:23 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.001 0.002 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"10.0.1.1" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:23:23 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"10.0.1.1" - realip_remote_addr:"172.0.1.9"可以看到这里取到了倒数第二个值为
remote_addr接着测试将最后两个值设置为可信,配置如下:
set_real_ip_from 172.0.1.0/24; set_real_ip_from 172.0.100.0/24; set_real_ip_from 10.0.1.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on;查看日志:
172.0.100.1 - - [18/Sep/2025:12:26:10 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"192.168.2.14" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:26:11 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.002 0.002 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"192.168.2.14" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:26:11 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"192.168.2.14" - realip_remote_addr:"172.0.1.9"可以看到这里取到了第一个值,也就是倒数第三个值为
remote_addr由此我们可以得出结论,当开启递归时,默认会从右往左进行查看,取第一个不可信的 IP 为
remote_addr,那么如果所有值都可信会发生什么呢?我们接着测试设置所有值为可信,配置如下:
set_real_ip_from 172.0.1.0/24; set_real_ip_from 172.0.100.0/24; set_real_ip_from 10.0.1.0/24; set_real_ip_from 192.168.2.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on;查看日志:
172.0.100.1 - - [18/Sep/2025:12:30:04 +0800] "GET / HTTP/1.0" 200 6283 493 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"192.168.2.14" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:30:04 +0800] "GET /api/web-info HTTP/1.0" 200 169 348 0.002 0.003 127.0.0.1:8080 httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"192.168.2.14" - realip_remote_addr:"172.0.1.9" 172.0.100.1 - - [18/Sep/2025:12:30:05 +0800] "GET /favicon.ico HTTP/1.0" 200 6283 408 0.000 - - httpx_forwarded_for:"192.168.2.14, 10.0.1.1, 172.0.100.1" - remote_addr:"192.168.2.14" - realip_remote_addr:"172.0.1.9"可以看到这里取了从左往右第一个值为
remote_addr。综合来看,当所有 IP 均可信 且 开启递归模式时,会选择从左往右的第一个值作为
remote_addr
2.3 实验总结
总结来看,如果配置了 real_ip_header X-Forwarded-For;,只有当来源 IP 配置为可信时,才会从 X-Forwarded-For 请求头中获取值,否则一律设置来源 IP 为 remote_addr。在这个前提下,不开启递归时,只会取最后一个值,所以日常配置中,推荐开启递归。当开启递归,且设置来源 IP 可信后,会从 X-Forwarded-For 请求头中从右往左选取第一个不在信任列表中的值作为客户端 IP,如果都在信任列表中,则取从右往左的最后一个 IP 作为 remote_addr。
综上,推荐在生产环境进行配置时,开启递归,并将所有内网代理机器 IP 添加至可信列表,具体配置如下:
# 这里的IP可以设置网段,也可以设置代理IP
set_real_ip_from 172.0.1.0/24;
set_real_ip_from 172.0.100.0/24;
set_real_ip_from 10.0.1.0/24;
# 设置获取请求头为 X-Forwarded-For,并开启递归
real_ip_header X-Forwarded-For;
real_ip_recursive on;
评论区