Nginx 获取真实客户端 IP:先划清可信代理边界
网站接入反向代理或负载均衡后,源站看到的连接地址通常是代理地址。于是很多配置直接读取 X-Forwarded-For,把最左侧内容当成访客 IP。问题在于:请求头可以由客户端自行发送,只有可信代理经过正确处理后交给源站的值,才具备相应可信度。本文从单层代理模型开始,不适用于直接照搬到复杂 CDN 链路。
AI生成概念配图:真实连接经过可信网关,伪造来源声明在边界被拦下,仅辅助理解,不代表真实界面或实测结果。
先把链路画清楚
假设公网客户端连接边缘 Nginx,边缘代理的内网地址为 10.20.0.10,再转发到源站。源站业务端口只允许边缘代理访问。这个网络限制很重要:它减少绕过边缘清洗、限流和头部重写的路径。配置前应明确每一跳的连接地址、谁生成转发头,以及哪些组件还能直接访问源站。
在这个“边缘直接接收公网客户端”的模型里,可以由边缘覆盖下游需要使用的头,而不是保留用户自报值。若边缘前面还有另一个可信代理,它的 $remote_addr 已不再是最终访客,这时必须重新设计整条信任链,不能继续套用以下简化配置。
# 边缘 Nginx 的代理 location 中 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;
源站只信任明确的代理地址
# 源站 http 或 server 上下文 # 10.20.0.10 仅为示例,请换成实际边缘地址 set_real_ip_from 10.20.0.10; real_ip_header X-Real-IP; real_ip_recursive off;
realip 模块会在连接对端属于可信来源时,使用指定字段重写客户端地址。不要为了“兼容所有来源”把 set_real_ip_from 写成 0.0.0.0/0 或整个不受控内网段。信任范围越大,能冒充客户端地址的发送方就越多。先用 nginx -V 确认构建具备相关模块,再按实际发行版验证。
多层代理使用 X-Forwarded-For 时,需要额外理解 real_ip_recursive:开启后,会沿受信任代理链寻找最后一个非可信地址,而不是简单拿列表第一项。每一跳的可信网段和追加规则都必须正确。第三方 CDN 的专用头也有同样前提:只信任官方公布、及时维护的来源地址,并防止绕过它直接访问源站。
同时记录重写前后的地址
# log_format 应放在 http 上下文 log_format ip_audit 'client=$remote_addr peer=$realip_remote_addr ' 'xff="$http_x_forwarded_for" "$request" $status'; access_log /var/log/nginx/ip_audit.log ip_audit;
这里 client 是处理后的地址,peer 保留原始连接地址,xff 则帮助排查请求头链路。原始头属于不可信输入,日志采集时要保留合适的转义,并避免把日志字符串直接拼进命令或查询。记录 IP 也涉及数据保留与访问控制,应根据业务必要性设置保留周期。
用伪造头验证,而不是只看正常请求
测试至少覆盖两种路径:经正常边缘请求时,源站应记录测试客户端的连接来源;在允许的隔离测试网络里,非可信机器即使发送自定义 X-Real-IP,也不应改变源站的地址认定。生产环境若不允许直连源站,验证结果应是连接被拒绝,而不是临时放开防火墙来方便测试。
发布前先执行 nginx -t,再通过维护流程重载,并检查日志与基于 IP 的限流是否仍符合预期。我的建议是把“原始连接来自哪里”和“代理声明客户端是谁”分别保存:前者帮助审计信任边界,后者服务业务判断。只有两者能解释得通,真实 IP 配置才算完成。


