要让后端获取最真实的客户端ip,必须先通过set_real_ip_from和real_ip_header可信还原$remote_addr,再用proxy_set_header透传;后端需主动读取x-real-ip或x-forwarded-for首项,不可依赖remote_addr。

要让后端拿到客户端“最真实”的 IP,不能只靠 proxy_set_header 简单转发,必须确保这个 IP 是从原始请求链路中**可信地提取出来、未被伪造、且未被中间代理污染**的。核心在于:Nginx 先识别出真实 IP(靠 set_real_ip_from + real_ip_header),再用 proxy_set_header 把它稳稳传出去。
先让 $remote_addr 变成真实 IP
这是最关键的前置步骤。如果 Nginx 前面有 CDN、WAF 或负载均衡器(比如阿里云 SLB、Cloudflare),$remote_addr 默认只是那个上游代理的出口 IP,不是用户本身。必须告诉 Nginx:“哪些 IP 是我信任的代理,它们传来的 X-Forwarded-For 或 X-Real-IP 我才信。”
- 在
http、server或location块中添加可信段,例如:set_real_ip_from 100.64.0.0/10;(阿里云私网回源段)set_real_ip_from 203.208.60.1;(某个 CDN 节点 IP) - 指定从哪个 Header 解析真实 IP:
real_ip_header X-Forwarded-For;(推荐,兼容多层)或real_ip_header X-Real-IP; - 开启递归解析:
real_ip_recursive on;—— 这样 Nginx 会从X-Forwarded-For的逗号分隔列表中,从右往左剔除所有已知代理 IP,最终把最左边那个非私有、非代理的 IP 设为新的$remote_addr和$realip_remote_addr
再用 proxy_set_header 把它透传出去
等 $remote_addr 已经是用户真实 IP 后,再在含 proxy_pass 的 location 块里配置 header。注意:必须放在 proxy_pass 行之前。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
-
proxy_set_header X-Real-IP $remote_addr;—— 单值清晰,后端可直接读取 -
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;—— 自动追加,保留完整链路(如203.0.113.1, 198.51.100.2),后端取第一个即可 - 避免写
$remote_addr直接赋给X-Forwarded-For,否则会覆盖上游已有的原始 IP
后端必须主动读取,不能依赖 REMOTE_ADDR
Nginx 只负责“发”,后端得自己“收”。几乎所有主流框架默认返回的都是连接发起方地址(即 Nginx 的 IP),不是 header 里的值。
- Node.js(Express):
req.headers['x-real-ip'] || req.headers['x-forwarded-for']?.split(',')[0].trim() - Python Flask:
request.headers.get('X-Real-IP')或request.environ.get('HTTP_X_REAL_IP') - Java Servlet:
request.getHeader("X-Real-IP"),不是getRemoteAddr() - PHP:
$_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'](带兜底)
额外提醒:别忽略安全与验证
伪造 X-Forwarded-For 极其容易。如果没有 set_real_ip_from 限定可信来源,real_ip_header 就形同虚设,proxy_set_header 传出去的可能是攻击者随便写的 IP。
- 上线前务必验证:
临时加一行add_header X-Debug-IP $remote_addr;,用 curl 或浏览器看响应头是否为你预期的真实公网 IP - 日志中也想记录真实 IP?启用
real_ip_module后,log_format中直接用$remote_addr就行,不用再从 header 解析 - 静态资源、API 接口、WebSocket 路由——只要需要识别客户端来源,这套逻辑都适用










