Nginx反向代理下后端获取真实IP需三者协同:Nginx配置proxy_set_header传递X-Real-IP和X-Forwarded-For头,后端框架(如Django、Flask、Express)启用代理头解析,并严格限定trusted_proxies可信代理网段,缺一不可。

在 Nginx 反向代理场景下,后端应用(如 Flask、Django、Node.js)默认收到的客户端 IP 是 Nginx 所在服务器的内网地址(比如 127.0.0.1 或 10.x.x.x),而非真实访问用户的公网 IP。这是因为 TCP 连接由 Nginx 与后端建立,原始请求被终止并重建。要让后端拿到真实 IP,关键不是只靠 proxy_set_header,而是**Nginx 正确传递 + 后端可信解析 X-Forwarded-For 等头 + 安全校验来源**,三者缺一不可。
正确配置 proxy_set_header 传递客户端信息
Nginx 需主动把客户端真实 IP 和代理链路写入 HTTP 请求头,发给后端。最常用且推荐的组合是:
- X-Real-IP:只设为客户端最外层真实 IP(适合简单单层代理)
- X-Forwarded-For:追加客户端 IP,并保留原有值(支持多级代理链路)
- X-Forwarded-Proto:告诉后端原始请求是 http 还是 https(影响 URL 生成)
示例配置:
location / {proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
注意:$proxy_add_x_forwarded_for 会在原有 X-Forwarded-For 值后追加 $remote_addr;如果上游没有该头,就只设 $remote_addr —— 这比硬写 $remote_addr 更健壮。
后端必须启用并信任对应头字段
仅 Nginx 发送头没用,后端框架需明确开启“从代理头读取客户端 IP”的功能,且**必须限定只信任来自可信代理的请求**,否则可能被伪造。
- Django:设置
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'),并配置USE_X_FORWARDED_HOST = True、USE_X_FORWARDED_PORT = True;更重要的是,通过SECURE_PROXY_SSL_HEADER和TRUSTED_PROXIES(Django 4.2+)或中间件限制可信代理 IP 段 - Flask(配合 Werkzeug 2.2+):使用
ProxyFix中间件,指定trusted_hosts(如['127.0.0.1', '192.168.0.0/16'])和x_for=1表示信任第一级X-Forwarded-For - Node.js(Express):用
app.set('trust proxy', ['127.0.0.1', '10.0.0.0/8'])显式声明可信代理网段,之后req.ip才会自动解析X-Forwarded-For
不设可信代理列表就直接读 X-Forwarded-For,等于把伪造 IP 的入口完全敞开。
避免常见陷阱:IP 被覆盖、伪造、日志错乱
以下问题高频出现,本质都是未区分“连接来源”和“原始来源”:
-
后端日志显示全是 127.0.0.1:Nginx 没配
proxy_set_header X-Real-IP或后端没启用代理头解析 -
日志里 IP 变成内网地址(如 172.18.0.1):Nginx 与后端容器通信走的是 Docker 网络,此时
$remote_addr是容器 IP;应改用$http_x_real_ip或确保 Nginx 在宿主机网络模式运行 -
X-Forwarded-For 出现多个 IP,取错位置:例如
X-Forwarded-For: 203.0.113.5, 198.51.100.3,其中前者是用户真实 IP,后者是上一级代理 IP;后端必须按“最左非可信 IP”逻辑取值,且依赖trusted_proxies列表跳过已知代理 -
HTTPS 页面混入 HTTP 资源:没传
X-Forwarded-Proto,后端生成链接时误判协议
验证是否生效:分步检查
不要只看后端日志,逐层确认:
- 在 Nginx access_log 中加
$http_x_forwarded_for和$remote_addr字段,确认进来的请求头含真实 IP - 用
curl -H "X-Forwarded-For: 1.2.3.4" http://yourdomain/测试后端是否返回 1.2.3.4 —— 若返回,说明未校验代理可信性,存在安全风险 - 查看后端实际收到的请求头(如 Django 的
request.META或 Express 的req.headers),确认X-Real-IP和X-Forwarded-For存在且合理 - 用公网设备访问,对比 Nginx 日志中的
$remote_addr(应为你的公网 IP)与后端记录的 IP 是否一致


















