Flask 如何用 nginx 反向代理 WSGI 服务器并配置 X-Forwarded 头与 ProxyFix
【免费下载链接】flaskThe Python micro framework for building web applications.项目地址: https://gitcode.com/gh_mirrors/fl/flask
当 Flask 应用部署到生产环境时,官方文档明确建议不要使用内置开发服务器,而是用 Gunicorn、Waitress 这类专用 WSGI 服务器来运行应用。此时通常还需要在 WSGI 服务器前面再放一层 HTTP 服务器——也就是 nginx 反向代理——由它来处理入站请求、TLS 以及安全和性能相关问题。这篇文章说明两件事:如何配置 nginx 把请求转发给本地 WSGI 服务器并添加X-Forwarded-头,以及如何在 Flask 应用中用 Werkzeug 的ProxyFix中间件信任并使用这些头。
为什么需要反向代理和 X-Forwarded 头
当使用反向代理时,代理会拦截所有外部请求并转发给本地 WSGI 服务器。此时从 WSGI 服务器和 Flask 应用的角度看,请求来自 HTTP 服务器所在的本地地址,而不再是远程客户端。为了让应用拿到真实的客户端信息,HTTP 服务器应当设置X-Forwarded-头把真实值传递给应用,然后再通过ProxyFix中间件告诉应用去信任并使用这些值(参见 部署总览 中对 "reverse proxy" 的定义)。
准备:让 WSGI 服务器监听本地地址
以 Gunicorn 为例(Waitress 同理,见 Gunicorn 文档、Waitress 文档):
$ cd hello-app $ python -m venv .venv $ . .venv/bin/activate $ pip install . # install your application $ pip install gunicorn # equivalent to 'from hello import app' $ gunicorn -w 4 'hello:app'其中hello:app表示from hello import app,格式为{module_import}:{app_variable};如果使用应用工厂模式,也可以写成函数调用形式,如gunicorn -w 4 'hello:create_app()'。-w指定 worker 进程数,默认只有 1 个 worker,文档给出的起始值是CPU * 2。
关键点:Gunicorn 不应以 root 运行,因此无法绑定 80/443 端口,这正是需要 nginx 这类反向代理放在前面的原因。文档同时警告:不要使用-b 0.0.0.0把 Gunicorn 绑定到所有外部 IP,否则可以绕过反向代理直接访问 WSGI 服务器。默认的http://127.0.0.1:8000监听即可,nginx 配置正是假设 WSGI 服务器监听在这个地址上。
启动后 Gunicorn 会输出(文档示例):
Starting gunicorn 20.1.0 Listening at: http://127.0.0.1:8000 (x) Using worker: sync Booting worker with pid: x配置 nginx 反向代理
nginx 可以用系统包管理器安装,Linux 上配置文件位于/etc/nginx/nginx.conf(不同操作系统路径可能不同,自行查找nginx.conf)。nginx 本身的安装和运行不在 Flask 文档范围内。
配置步骤:
- 删除或注释掉现有的
server段; - 添加新的
server段,用proxy_pass指向 WSGI 服务器监听的地址; - 用
proxy_set_header添加X-Forwarded-头:
server { listen 80; server_name _; location / { proxy_pass http://127.0.0.1:8000/; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Prefix /; } }四个头分别传递真实客户端 IP(X-Forwarded-For)、协议(X-Forwarded-Proto)、主机名(X-Forwarded-Host)和路径前缀(X-Forwarded-Prefix)。如果 WSGI 服务器监听地址不是http://127.0.0.1:8000,替换proxy_pass的目标即可。
用 ProxyFix 让 Flask 信任这些头
nginx 设置好头之后,还需要在应用中启用 Werkzeug 提供的ProxyFix中间件。写法是包装应用的wsgi_app属性,而不是包装app本身——这样app仍指向你的 Flask 应用而不是中间件,你可以继续直接配置app:
from werkzeug.middleware.proxy_fix import ProxyFix app.wsgi_app = ProxyFix( app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1 )参数含义是"每一级代理负责设置哪些头":x_for、x_proto、x_host、x_prefix表示各有多少个代理设置了X-Forwarded-For、X-Forwarded-Proto、X-Forwarded-Host、X-Forwarded-Prefix。上面的示例对应"前面只有一级代理",即本文的 nginx 场景。
文档对此有明确的安全约束,必须遵守:
- 只有应用确实位于代理后面时才应用这个中间件;
- 必须设置正确的代理层数——并非所有代理都会设置全部头,而入站的
X-Forwarded-头可以被伪造,必须通过参数声明有几层代理在设置每个头,中间件才知道该信任多少层; - 如果这个配置弄错了,可能成为安全问题。
验证代理是否生效
Flask 文档没有单独的"nginx 部署成功"检查清单,验证方式是按文档给出的链路逐步确认:
WSGI 服务器已先于 nginx 启动,并且
proxy_pass指向的地址与 WSGI 服务器Listening at输出的地址一致(本例为http://127.0.0.1:8000);浏览器或
curl通过 nginx 访问站点,请求能返回应用页面,说明proxy_pass链路通了。测试时可以按 nginx 文档 的建议模拟域名:在 Linux 的/etc/hosts(或现代 Linux 系统对.localhost域名的处理)中加一行,例如:127.0.0.1 hello.localhost在应用内部读取请求上下文时,确认拿到的是代理传入的真实值(主机、协议、客户端 IP 等),而不是本地回环地址——这一步对应 proxy_fix 文档 描述的"代理把外部请求转发给本地 WSGI 服务器"的场景;如果读到的
Host等值是 nginx 本地地址而不是客户端真实值,说明ProxyFix未生效或参数与实际代理层数不符。
限制与边界
- 不要把 WSGI 服务器绑定到
0.0.0.0:文档明确说明在反向代理场景下这样做会绕过代理;WSGI 服务器应只监听127.0.0.1,由 nginx 的listen 80对外服务。 - nginx 配置示例是文档给出的基础形式(
server_name _匹配任意主机名);生产环境中域名解析与配置不在 Flask 文档范围内,需要按 nginx 自身文档补充。 ProxyFix的参数必须与实际代理链匹配:本文主路径是"nginx 一层代理",参数取 1;如果实际链路上代理层数不同,需要按 proxy_fix 文档重新确认每层设置了哪些头。
下一步
部署文档建议:如果前面还有代理可能改写Host头,可以设置TRUSTED_HOSTS来限制应用允许的Host取值,配合ProxyFix明确告知应用该信任哪些代理值,详见 web-security 文档。
【免费下载链接】flaskThe Python micro framework for building web applications.项目地址: https://gitcode.com/gh_mirrors/fl/flask
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考