直接读取http_x_real_ip不安全,因其可被伪造且仅反映直连nginx的客户端ip;正确做法是启用nginx realip模块并配置set_real_ip_from与real_ip_header,结合x-forwarded-for分层校验可信代理链。

Python后端直接读HTTP_X_REAL_IP不安全
很多 Python Web 项目一上来就写 request.headers.get('X-Real-IP') 或 request.META.get('HTTP_X_REAL_IP'),以为这就是用户真实 IP。但这个头完全由 Nginx 添加,前提是它被配置了 proxy_set_header X-Real-IP $remote_addr —— 而且这个值只反映「直连 Nginx 的客户端」IP,比如 CDN 节点或中间代理的出口 IP,并非物理用户。
更危险的是:如果 Nginx 没启用 realip 模块,或没配 set_real_ip_from,那么后端拿到的 X-Real-IP 就可能被前端伪造(例如 curl -H 'X-Real-IP: 1.2.3.4'),导致日志、限流、风控全失效。
所以别无脑信任 X-Real-IP,除非你确认 Nginx 已用 realip 模块重写了 $remote_addr。
正确解析X-Forwarded-For要分层判断
X-Forwarded-For 是个逗号分隔的字符串,形如 203.0.113.195, 198.51.100.101, 203.0.113.68,代表请求经过的每一跳代理的出口 IP。最左边是原始客户端,最右边是最后一级代理(即直连你的 Nginx 的那个)。
但不能简单取第一个 —— 因为任意中间代理或恶意请求者都能在开头追加伪造 IP。必须结合可信代理链来裁剪:
- 如果你的架构是「用户 → CDN → Nginx → Python 应用」,那只有 CDN 的出口 IP 是可信的,其他都不可信
- Nginx 配置里必须明确指定
set_real_ip_from为 CDN 的 IP 段,否则realip模块不会动$remote_addr - Python 中应优先使用
request.headers.get('X-Forwarded-For', '').split(',')[0].strip(),仅当确认整个链路第一跳(如 CDN)可信时才这么用 - 更稳妥的做法是:先检查
X-Forwarded-For长度,再结合已知可信代理数量做截断,例如已知最多两层代理,就取倒数第 3 个
Django/Flask 中获取真实 IP 的推荐写法
不要依赖单一 header,按可信度降序 fallback:
在 Django 中,建议这样写:
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0].strip()
else:
ip = request.META.get('HTTP_X_REAL_IP') or request.META.get('REMOTE_ADDR')
return ip
在 Flask 中类似:
def get_client_ip():
if request.headers.get('X-Forwarded-For'):
return request.headers['X-Forwarded-For'].split(',')[0].strip()
return request.headers.get('X-Real-IP') or request.remote_addr
注意两点:
-
request.remote_addr在 Flask 中等价于 Nginx 的$remote_addr,仅当没有反向代理或 Nginx 启用了realip模块并重写过它时才可靠 - 所有这些值都可能为空或被污染,生产环境必须配合 Nginx 的
set_real_ip_from+real_ip_header配置才能闭环
关键:Nginx 必须配realip模块并设set_real_ip_from
光靠 proxy_set_header 加 header 不够,后端看到的 REMOTE_ADDR 还是 Nginx 自己的 socket 对端地址(即上一级代理 IP)。要让它变成用户真实 IP,必须启用 http_realip_module 并正确配置:
确认模块已加载:nginx -V 2>&1 | grep -o with-http_realip_module
在 server 或 http 块中添加:
set_real_ip_from 10.0.0.0/8; # 你的可信代理网段,比如私有云 LB set_real_ip_from 203.0.113.0/24; # CDN 公网 IP 段 real_ip_header X-Forwarded-For; real_ip_recursive on;
其中 real_ip_recursive on 表示从右往左逐个剔除可信代理 IP,最终剩下最左边那个不可信的(即用户真实 IP);如果只有一层代理,off 也能取到最后一个,但多层时必须 on 才能穿透。
漏掉 set_real_ip_from 是最常见错误——它不是可选配置,而是安全边界。没它,realip 模块根本不会修改 $remote_addr。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











