要正确获取客户端真实ip,必须结合可信代理配置与realip模块校验:先用set_real_ip_from声明可信代理网段,再通过real_ip_header x-forwarded-for和real_ip_recursive on递归提取最左合法ip赋给$remote_addr,后端应用应直接信任该值而非重复解析x-forwarded-for。

在 Nginx 作为反向代理时,客户端真实 IP 通常被隐藏在 X-Forwarded-For 请求头中。直接使用 $remote_addr 只能得到上一级代理的 IP(比如负载均衡器或 CDN),而非用户真实 IP。要正确获取真实 IP,需结合可信代理链和头信息解析。
理解 X-Forwarded-For 的格式与风险
X-Forwarded-For 是一个由逗号分隔的 IP 列表,形如:X-Forwarded-For: 203.0.113.45, 198.51.100.2, 192.0.2.1
最左边是发起请求的原始客户端 IP,后续是各级代理添加的“上一跳”地址。但该头可被客户端伪造,因此不能直接信任 —— 必须只从**可信代理**追加的字段中提取。
配置 trusted_proxies 并启用 real_ip 模块
Nginx 的 ngx_http_realip_module(通常默认启用)用于替换 $remote_addr 和相关变量。关键步骤:
- 明确列出你控制的上游代理 IP 或网段(如公司内网网关、云厂商 LB 内网地址),写入
set_real_ip_from - 指定用于提取真实 IP 的头字段,常用
real_ip_header X-Forwarded-For - 设定取值策略:用
real_ip_recursive on表示递归查找最后一个可信代理前的 IP;设为off则取列表中最左(即最原始)的 IP —— 推荐on,更安全
示例配置:
set_real_ip_from 10.0.0.0/8; # 内网代理网段 set_real_ip_from 172.16.0.0/12; set_real_ip_from 192.168.0.0/16; set_real_ip_from 198.51.100.1; # 具体 LB IP real_ip_header X-Forwarded-For; real_ip_recursive on;
验证真实 IP 是否生效
配置后,$remote_addr 就会变成客户端真实 IP(前提是请求经过你声明的可信代理)。可在日志中测试:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
观察日志中 $remote_addr 是否为你期望的真实客户端 IP,同时比对 $http_x_forwarded_for 内容是否合理(如含多个 IP 且最左为公网地址)。
应用层也要配合处理
即使 Nginx 正确设置了 $remote_addr,后端应用(如 PHP、Node.js、Python)仍可能读取 X-Forwarded-For 自行解析。这时务必让应用信任 Nginx 已处理后的 REMOTE_ADDR(或对应环境变量),而不是再次解析 X-Forwarded-For —— 否则绕过 Nginx 的可信校验,引入安全隐患。
例如 Django 需设置 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 并启用 USE_X_FORWARDED_FOR = True,但前提仍是 Nginx 已完成 IP 替换;Flask 中推荐直接使用 request.remote_addr(它底层依赖 WSGI server 提供的 REMOTE_ADDR,而该值已被 Nginx 的 realip 模块修正)。











