获取的全是 172 开头的假 IP?Nginx 反代后,Flask 怎么才能拿到真实访客 IP?

5 阅读2分钟

📖 背景 监控大屏跑起来了。我想在后台加个日志,记录访客的来源 IP 防刷。 结果一看日志傻眼了:所有记录的 IP 都是 172.18.0.x。 我用的是 Nginx 反代 Flask 容器,request.remote_addr 拿到的全是 Docker 内网 IP,根本不是访客的真实公网 IP。

🔍 原因 小学生版解释: 访客(真实 IP)把信交给了门卫(Nginx)。Nginx 把信转交给里面的收发室(Flask 容器)。 Flask 看到信,问:“谁给的?” Nginx 说:“是我给的(172.18.0.x)”。 真实访客的 IP 根本没写在信封上。Flask 被骗了。

🛠 解决 两步。Nginx 加头,Flask 解析。

  1. Nginx 传递真实 IP 修改 Nginx 配置:
location / {
    proxy_pass http://dashboard:5000;
    # 告诉 Flask,真实客户端 IP 是什么
    proxy_set_header X-Real-IP $remote_addr;
    # 如果有多层代理,用这个追加
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
  1. Flask 信任 Nginx 的头 修改 Flask 代码(通常是 app.py 或 init.py):
from flask import Flask, request
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
# 告诉 Flask:我前面有 1 层代理(Nginx),请从 X-Forwarded 头里取真实 IP
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)

@app.route('/')
def index():
    # 现在拿到的就是真实公网 IP 了
    return f"Your Real IP: {request.remote_addr}"

✅ 验证效果 重启 Nginx 和 Flask 容器。 用手机(不用 WiFi,用 4G)访问大屏。 看 Flask 日志,或者页面输出: 预期输出:Your Real IP: 你的4G公网IP(不再是 172 开头)。

💣 三个坑 配了 Nginx 忘了配 ProxyFix:Flask 默认出于安全考虑,不信任代理头。少写一行 ProxyFix,拿到的还是内网 IP。

remoteaddrvsremote_addr vs proxy_add_x_forwarded_for:X-Real-IP 适合单层代理。如果前面有 Cloudflare 和 Nginx 两层,必须用 X-Forwarded-For 列表,并取第一个 IP。

恶意伪造:代理头是可以伪造的(客户端自己带个假 IP 过来)。如果有严格的安全需求,必须配合 Nginx 的 real_ip_module 模块处理。