Flask生产部署:uWSGI+Nginx在Ubuntu 16.04上的完整实践
1. 项目概述为什么 Flask 应用不能直接暴露在公网你写完一个 Flask 应用本地flask run跑起来浏览器一开页面亮了——高兴三秒后问题来了上线部署时直接python app.py或flask run --host0.0.0.0 --port5000放到服务器上立刻被运维同事按住肩膀说“停别动先关掉。”这不是矫情是血泪教训。Flask 自带的开发服务器Werkzeug 的WSGIServer本质是个单线程、单进程、无超时控制、无连接池、无静态文件优化、不支持 HTTP/2、不处理长连接复用、甚至默认不校验 Host 头的“玩具”。它连并发 10 个请求都可能卡死更别说应对真实用户访问时的慢速攻击、HEAD 请求泛滥、恶意 User-Agent 扫描、或一个前端页面加载 20 个 JS/CSS 图片资源带来的连接风暴。我最早在 Ubuntu 16.04 上部署图书管理后台时就吃过这个亏用户刚点登录按钮整个服务就“假死”两分钟日志里只有一行INFO:werkzeug: * Running on http://0.0.0.0:5000/像块沉默的墓碑。真正能扛住生产环境的从来不是框架自带的服务器而是由专业 WSGI 容器 反向代理组成的双层结构。uWSGI 是其中最成熟、最可控的 WSGI 容器之一它负责把 Python 字节码安全地载入内存、管理多进程/多线程/异步协程、做请求路由分发、处理信号、优雅重启、内存监控而 Nginx 则站在最外层干它最擅长的事高并发连接管理epoll/kqueue、静态文件零拷贝发送、SSL/TLS 终结、负载均衡、请求限速、防 CC 攻击、缓存压缩、HTTP/2 支持、以及最重要的——把用户发来的 HTTP 请求精准、高效、安全地转发给后端的 uWSGI 进程。Ubuntu 16.04 这个版本选得非常实在它是 LTS长期支持版内核稳定、软件源成熟、社区文档丰富尤其对 Python 3.5 和早期 uWSGI/Nginx 版本兼容性极佳至今仍有大量企业级内部系统运行其上。这不是怀旧而是权衡——在稳定性、安全性、维护成本之间它划出了一条清晰的基准线。所以“How To Serve Flask Applications with uWSGI and Nginx on Ubuntu 16.04” 这个标题表面是教配置步骤内核其实是传递一个工程共识Web 应用的生产就绪Production-Ready始于对每一层职责的敬畏与隔离。你不需要成为 Nginx 内核专家但必须清楚它为何不能被绕过你不必手写 uWSGI 源码但得明白每个配置项背后是哪类线上故障在倒逼它存在。这篇文章就是带你亲手搭起这座桥并告诉你桥墩怎么打、缆绳怎么系、风大时哪里会晃。2. 整体架构设计与方案选型逻辑2.1 为什么是 uWSGI 而不是 Gunicorn在 Python Web 部署领域uWSGI 和 Gunicorn 是两大主流 WSGI 容器。很多新手看到 Gunicorn 配置简洁、文档清爽就默认选它。但在 Ubuntu 16.04 这个特定环境下uWSGI 是更优解理由很硬核第一进程模型控制粒度更细。Gunicorn 默认只提供sync同步阻塞、gevent协程、eventlet协程三种工作模式而 uWSGI 支持processes多进程、threads多线程、gevent、asyncio、coroutine甚至混合模式如processes4 threads2。这意味着你能精确控制每个 worker 占用多少 CPU 核心、多少内存、是否启用线程锁。我在部署一个需要调用外部 OCR API 的 Flask 后台时发现纯多进程下 OCR SDK 初始化耗时巨大而开启少量线程后同一进程内复用 SDK 实例QPS 直接翻倍。Gunicorn 做不到这种精细调度。第二热重载与优雅重启机制更可靠。uWSGI 的--touch-reload参数可监听任意文件变化比如app.py或config.py触发零停机重启它的--master模式下主进程管理所有 worker收到SIGTERM时会先停止接收新请求等所有 worker 处理完当前请求再退出避免请求中断。Gunicorn 的--reload在 Ubuntu 16.04 的 systemd 环境下偶发出现子进程残留导致端口占用无法释放而 uWSGI 的--die-on-term配合--vacuum退出时自动清理 Unix socket 和 pid 文件几乎从不翻车。第三与 Nginx 的 Unix socket 通信更原生、更高效。虽然两者都支持 TCP 和 Unix socket但 uWSGI 对 Unix socket 的权限控制chmod-socket、chown-socket和路径管理socket、stats、pidfile分离比 Gunicorn 更细致。Nginx 通过unix:/run/uwsgi.sock访问 uWSGI比走127.0.0.1:8000少一次 TCP/IP 协议栈实测在 1000 并发下延迟降低 12%~18%这对响应时间敏感的图书检索接口至关重要。提示这不是贬低 Gunicorn。如果你的项目是轻量 API 服务、团队熟悉 Docker、且未来计划迁移到 KubernetesGunicorn 因其镜像小、启动快、与gunicorn.conf.py配置风格统一反而是更现代的选择。但本文聚焦 Ubuntu 16.04 物理机/虚拟机部署uWSGI 的稳定性和可控性是首选。2.2 为什么必须用 Nginx 而非直接暴露 uWSGI有人会问“uWSGI 本身就能监听 80 端口为啥还要套一层 Nginx” 这是个关键误区。uWSGI 的http模式即uwsgi --http :80只是个调试功能它把 uWSGI 当成 HTTP 服务器用完全绕过了其核心优势——专注 WSGI 协议处理。这会导致三个致命问题安全漏洞放大uWSGI 的 HTTP 模块不实现完整的 HTTP/1.1 规范对畸形请求如超长 Header、分块编码错误、HTTP pipelining处理粗糙容易触发缓冲区溢出或拒绝服务。Nginx 经过十年以上互联网流量锤炼对各类 HTTP 边界情况有成熟防御策略。静态文件性能灾难uWSGI 处理/static/css/app.css这类请求要经过 Python 解释器、Flask 路由匹配、文件系统读取、MIME 类型判断、响应头组装……而 Nginx 用sendfile()系统调用直接从磁盘 DMA 到网卡零拷贝速度差一个数量级。无法做反向代理的核心能力SSL 终结Let’s Encrypt 自动续期、客户端 IP 透传X-Forwarded-For、请求头过滤屏蔽危险 UA、速率限制防止暴力登录、WebSocket 升级支持为后续 Vue 前端实时通知预留、甚至简单的 A/B 测试路由if ($arg_version v2) { proxy_pass http://backend_v2; }——这些都不是 uWSGI 的职责硬塞进去只会让架构臃肿脆弱。所以标准拓扑永远是用户 → Nginx反向代理 静态服务 SSL → uWSGIWSGI 容器 → Flask App业务逻辑。这是经过千万级 PV 验证的黄金分层。2.3 Ubuntu 16.04 的特殊考量与版本锁定Ubuntu 16.04 发布于 2016 年 4 月EOL终止支持虽在 2021 年 4 月但其软件源中nginx1.10.3、python3.5、pip8.1.1版本稳定与当时主流的uwsgi2.0.14兼容性极佳。强行升级到更新版 Nginx如 1.18可能导致nginx -t配置检测失败因新版引入了stream模块语法或ssl_protocols默认值变更uWSGI 编译时找不到nginx的ngx_http_uwsgi_module头文件因路径结构变化Python 3.5 的venv模块与新版setuptools冲突导致虚拟环境创建失败。因此本文所有操作均基于官方源原版包不启用universe或backports源不手动编译任何组件。这是对生产环境“最小变更原则”的尊重——能用 apt 装好的绝不自己编译能用系统 Python 的绝不装 pyenv能用.deb包的绝不 pip install。稳定压倒一切。3. 核心细节解析与实操要点3.1 环境初始化系统级准备与权限隔离在 Ubuntu 16.04 上一切始于干净的系统状态。不要跳过这一步否则后续 80% 的权限错误、socket 无法访问、日志写入失败都源于此。首先更新系统并安装基础工具sudo apt update sudo apt upgrade -y sudo apt install -y python3-pip python3-dev build-essential libssl-dev libffi-dev nginx注意python3-dev是编译 uWSGI 必需的 C 头文件libssl-dev和libffi-dev是 uWSGI 调用 OpenSSL 和 CFFI用于调用 C 库的依赖。漏掉任何一个pip install uwsgi都会报错fatal error: Python.h: No such file or directory或ffi.h not found。接着创建专用用户与组绝对禁止用 root 或 www-data 运行 Flask 应用sudo adduser --disabled-password --gecos flaskuser sudo usermod -a -G www-data flaskuser这里flaskuser是应用运行者www-data是 Nginx 默认工作组。usermod -a -G是关键-a表示追加append避免覆盖用户原有组-G www-data让flaskuser能读写 Nginx 的日志目录/var/log/nginx和 socket 文件目录/run/uwsgi。如果只用usermod -G www-data flaskuser会清空flaskuser的其他组如sudo导致后续无法执行sudo systemctl命令。然后创建应用目录并设置严格权限sudo mkdir -p /var/www/myflaskapp sudo chown -R flaskuser:www-data /var/www/myflaskapp sudo chmod -R 750 /var/www/myflaskapp750权限意味着所有者flaskuser可读写执行组www-data可读执行其他人无任何权限。这是安全基线——Nginx 进程以www-data用户运行需要读取myflaskapp下的uwsgi.ini和静态文件uWSGI 进程以flaskuser运行需要写入日志和 socket。任何宽松权限如777都是在邀请黑客进门。实操心得我曾在线上环境因忘记chown -R导致 uWSGI 启动时提示bind(): Permission denied。排查了两小时才发现/var/www/myflaskapp目录属主是rootflaskuser无权在其中创建uwsgi.sock。记住Linux 权限是递归的父目录没权限子目录再开放也白搭。3.2 Flask 应用结构化改造从脚本到可部署包一个能直接flask run的app.py离生产部署还差三步模块化、配置分离、入口标准化。假设原始app.py是这样的from flask import Flask app Flask(__name__) app.route(/) def hello(): return Hello World! if __name__ __main__: app.run()这不行。必须重构为标准包结构/var/www/myflaskapp/ ├── myflaskapp/ # Python 包必须含 __init__.py │ ├── __init__.py # 创建 Flask 实例 │ ├── models.py # 数据库模型如有 │ └── routes.py # 路由定义 ├── config.py # 配置文件开发/生产分离 ├── wsgi.py # WSGI 入口uWSGI 加载点 └── requirements.txt # 依赖清单myflaskapp/__init__.py是核心from flask import Flask import os def create_app(config_nameproduction): app Flask(__name__) # 根据 config_name 加载配置 if config_name development: app.config.from_object(config.DevelopmentConfig) else: app.config.from_object(config.ProductionConfig) # 初始化扩展如 SQLAlchemy, Redis # from .extensions import db, redis_client # db.init_app(app) # redis_client.init_app(app) # 注册蓝图 from .routes import main_bp app.register_blueprint(main_bp) return appconfig.py实现配置分离import os class Config: SECRET_KEY os.environ.get(SECRET_KEY) or dev-secret-key # 公共配置 class DevelopmentConfig(Config): DEBUG True SQLALCHEMY_DATABASE_URI sqlite:///dev.db class ProductionConfig(Config): DEBUG False SQLALCHEMY_DATABASE_URI os.environ.get(DATABASE_URL) or sqlite:///prod.db # 关键禁用 Flask 自动重载避免 uWSGI 多进程冲突 USE_RELOADER Falsewsgi.py是 uWSGI 的唯一入口from myflaskapp import create_app # 注意这里必须传入 production不能用 os.getenv()因为 uWSGI 不读取 shell 环境变量 application create_app(production)为什么叫application因为 uWSGI 默认寻找名为application的可调用对象PEP 3333 规定。名字错了uWSGI 启动就报ImportError: No module named wsgi或callable not found。最后requirements.txt必须锁定版本Flask1.1.4 SQLAlchemy1.3.24 uWSGI2.0.19 # 不要写 flask1.0生产环境必须确定版本pip freeze requirements.txt生成的文件常含pkg-resources0.0.0这种无用项手动删掉。版本锁定是避免某天pip install拉到不兼容新版导致服务崩溃的唯一手段。3.3 uWSGI 配置详解每一个参数都是线上故障的预防针uWSGI 的配置是灵魂.ini文件不是随便堆参数每个字段都对应一个真实风险点。以下是最小可行且安全的uwsgi.ini# /var/www/myflaskapp/uwsgi.ini [uwsgi] # 1. 应用定位 module wsgi:application master true processes 4 threads 2 # 2. Socket 通信关键必须用 Unix socket socket /run/uwsgi.sock chmod-socket 664 chown-socket flaskuser:www-data vacuum true die-on-term true # 3. 进程管理 pidfile /var/run/uwsgi.pid daemonize /var/log/uwsgi/myflaskapp.log log-maxsize 10000000 log-backupname /var/log/uwsgi/myflaskapp.log.old # 4. 安全与健壮性 uid flaskuser gid www-data harakiri 30 max-requests 1000 limit-as 512 reload-on-rss 256 # 5. 启动与停止 touch-reload /var/www/myflaskapp/wsgi.py逐条解析module wsgi:application告诉 uWSGI 去wsgi.py文件里找application变量。路径必须相对于 uWSGI 启动目录即chdir指定的目录否则报错。master true启用 master 进程。没有它worker 进程挂了就真挂了无法自动拉起。processes 4启动 4 个 worker 进程。经验公式CPU 核心数 × 2 1。Ubuntu 16.04 虚拟机常见 2 核所以设 4。太多浪费内存太少扛不住并发。threads 2每个 worker 内启 2 个线程。适合 I/O 密集型如数据库查询、API 调用但不要超过 4否则 GIL 锁竞争加剧。socket /run/uwsgi.sockUnix socket 路径。/run/是 tmpfs 内存文件系统比/tmp/更快更安全。chmod-socket 664socket 文件权限设为rw-rw-r--确保flaskuser所有者和www-data所属组都能读写其他人只读。660会导致 Nginx 无法连接因 Nginx 进程属于www-data组但 socket 所属组权限是---。vacuum true退出时自动删除 socket 文件和 pid 文件。否则下次启动报Address already in use。die-on-term true收到SIGTERMsystemd 停止服务时发的信号时uWSGI 主进程立即退出不等待 worker。配合vacuum保证干净退出。pidfile和daemonize记录主进程 PID 和日志路径。/var/log/uwsgi/目录需提前创建并授权sudo mkdir -p /var/log/uwsgi sudo chown flaskuser:www-data /var/log/uwsgi。harakiri 30救命参数如果某个请求处理超过 30 秒uWSGI 强制杀死该 worker 进程防止一个慢请求拖垮所有连接。这是对抗数据库锁表、外部 API 假死的终极保险。max-requests 1000每个 worker 处理 1000 个请求后自动重启。防止内存泄漏累积Python 的引用计数虽好但 C 扩展或第三方库可能有泄漏。limit-as 512每个 worker 进程最大内存 512MB。超限则被 OOM Killer 杀死。结合reload-on-rss 256RSS 内存达 256MB 时主动 reload双重防护。touch-reload监听wsgi.py文件修改。部署新代码后只需touch /var/www/myflaskapp/wsgi.pyuWSGI 自动滚动重启无需systemctl restart。注意chown-socket flaskuser:www-data中的www-data是组名不是用户名。Ubuntu 16.04 的 Nginx 默认以www-data用户和www-data组运行所以 socket 所属组必须是www-data权限664才能让 Nginx 连上。这是新手最容易填错的地方。4. 实操过程与核心环节实现4.1 完整部署流程从零到服务启动现在把所有碎片拼成完整链条。全程以flaskuser身份操作sudo su - flaskuser避免权限混乱。步骤 1上传并初始化应用代码# 切换到应用目录 cd /var/www/myflaskapp # 假设代码已通过 git clone 或 scp 上传到此目录 # 确保结构正确 ls -l # 应看到 myflaskapp/ config.py wsgi.py requirements.txt uwsgi.ini # 创建 Python 虚拟环境强烈推荐隔离依赖 python3 -m venv venv source venv/bin/activate # 安装依赖注意uWSGI 必须在虚拟环境中安装否则找不到 Python.h pip install --upgrade pip pip install -r requirements.txt # 此时 uWSGI 会被安装到 venv 中步骤 2测试 uWSGI 独立运行# 临时前台运行看是否能加载应用 uwsgi --ini uwsgi.ini --http :8000 --protocolhttp打开浏览器访问http://your-server-ip:8000。如果看到 Hello World!说明 uWSGI 和 Flask 通信成功。此时按CtrlC停止。提示--http :8000是临时调试用生产环境必须去掉只留socket。步骤 3创建 uWSGI systemd 服务单元Ubuntu 16.04 使用 systemd 管理服务。创建/etc/systemd/system/myflaskapp.service[Unit] DescriptionuWSGI instance to serve myflaskapp Afternetwork.target [Service] Userflaskuser Groupwww-data WorkingDirectory/var/www/myflaskapp EnvironmentPATH/var/www/myflaskapp/venv/bin ExecStart/var/www/myflaskapp/venv/bin/uwsgi --ini /var/www/myflaskapp/uwsgi.ini [Install] WantedBymulti-user.target关键点User和Group必须与 uWSGI 配置中的uid/gid一致WorkingDirectory是 uWSGI 查找uwsgi.ini和wsgi.py的基准路径EnvironmentPATH...确保 systemd 能找到虚拟环境中的uwsgi可执行文件ExecStart必须用绝对路径指向虚拟环境里的uwsgi不能只写uwsgi。启用并启动服务sudo systemctl daemon-reload sudo systemctl enable myflaskapp sudo systemctl start myflaskapp sudo systemctl status myflaskapp # 检查是否 active (running)查看日志sudo journalctl -u myflaskapp -f。正常应看到*** Starting uWSGI ***和spawned uWSGI master process。步骤 4配置 Nginx 反向代理编辑/etc/nginx/sites-available/myflaskappserver { listen 80; server_name your-domain.com; # 替换为你的域名或 IP # 静态文件直接由 Nginx 服务 location /static { alias /var/www/myflaskapp/myflaskapp/static; expires 1h; add_header Cache-Control public, immutable; } # 所有其他请求转发给 uWSGI location / { include uwsgi_params; uwsgi_pass unix:/run/uwsgi.sock; uwsgi_read_timeout 30; uwsgi_send_timeout 30; # 透传客户端真实 IP uwsgi_param SCRIPT_NAME ; uwsgi_param UWSGI_SCRIPT wsgi:application; uwsgi_param UWSGI_CHDIR /var/www/myflaskapp; uwsgi_param HTTP_X_FORWARDED_FOR $remote_addr; uwsgi_param HTTP_X_REAL_IP $remote_addr; } }启用站点sudo ln -sf /etc/nginx/sites-available/myflaskapp /etc/nginx/sites-enabled/ sudo nginx -t # 必须通过否则不 reload sudo systemctl reload nginx此时访问http://your-server-ip应该看到 Flask 页面。如果 502 Bad Gateway90% 是 uWSGI socket 权限或路径问题。步骤 5添加 HTTPSLet’s EncryptUbuntu 16.04 自带certbotsudo apt install -y python-certbot-nginx sudo certbot --nginx -d your-domain.comCertbot 会自动修改 Nginx 配置添加 SSL 证书和重定向规则。完成后https://your-domain.com即可访问。4.2 关键参数验证与性能基线测试部署不是终点而是观测起点。必须验证核心参数是否生效验证 Unix socket 权限ls -l /run/uwsgi.sock # 应输出srw-rw-r-- 1 flaskuser www-data 0 ... /run/uwsgi.sock # 注意开头的 s 表示 socket 类型权限 664 即 rw-rw-r--验证 uWSGI 进程归属ps aux | grep uwsgi # 应看到类似 # flaskuser 1234 0.0 1.2 123456 7890 ? S 10:00 0:00 uwsgi --ini /var/www/myflaskapp/uwsgi.ini # flaskuser 1235 0.0 0.8 123456 5678 ? S 10:00 0:00 uwsgi --ini /var/www/myflaskapp/uwsgi.ini # ... 共 4 个 worker 进程用户都是 flaskuser验证 Nginx 连接# 查看 Nginx 是否在监听 80 端口 sudo ss -tlnp | grep :80 # 查看 Nginx 进程是否以 www-data 运行 ps aux | grep nginx | grep master # 应看到www-data 5678 ... nginx: master process /usr/sbin/nginx简单压力测试ab 工具# 安装 abApache Bench sudo apt install -y apache2-utils # 测试 100 并发1000 次请求 ab -n 1000 -c 100 http://localhost/关注结果中的Requests per secondQPS和Time per request平均延迟。在 Ubuntu 16.04 虚拟机2核4G上一个空 Flask 应用QPS 应稳定在 800~1200延迟 20ms。如果 QPS 200大概率是 uWSGI 进程数不足或 Nginx 配置错误。4.3 日志体系搭建让问题自己说话生产环境没有日志等于蒙眼开车。必须建立三层日志uWSGI 应用日志由uwsgi.ini中daemonize指定记录 Flask 抛出的异常、SQLAlchemy 查询日志需配置、自定义app.logger.error()。Nginx 访问日志/var/log/nginx/access.log记录每个请求的 IP、URL、状态码、耗时、User-Agent。Nginx 错误日志/var/log/nginx/error.log记录 502/503 错误、socket 连接失败、SSL 握手失败等。关键技巧在nginx.conf的http块中添加日志格式区分前后端log_format upstreamlog [$time_local] $remote_addr - $remote_user [$request] $status $body_bytes_sent $http_referer $http_user_agent rt$request_time uct$upstream_connect_time uht$upstream_header_time urt$upstream_response_time; access_log /var/log/nginx/myflaskapp_access.log upstreamlog;upstream_response_timeurt是 Nginx 从 uWSGI 收到响应的时间如果它很大1s而request_timert更大说明瓶颈在 uWSGI 或 Flask如果rt大而urt小说明瓶颈在 Nginx如 SSL 加解密、静态文件读取。uWSGI 日志中harakiri事件会明确打印harakiri on an worker这是性能调优的黄金线索。实操心得有一次线上图书搜索变慢我查upstream_response_time平均 800ms但request_time平均 1200ms。对比发现upstream_header_timeuht高达 400ms说明 uWSGI 响应头生成慢。最终定位到 Flask 的after_request钩子里有个未缓存的数据库查询。日志字段就是破案的指纹。5. 常见问题与排查技巧实录5.1 502 Bad Gateway最频繁的“拦路虎”现象浏览器显示 502Nginx 错误日志出现connect() to unix:/run/uwsgi.sock failed (111: Connection refused)或no live upstreams while connecting to upstream。排查路径按优先级检查 uWSGI 服务状态sudo systemctl status myflaskapp # 如果 inactive看 journalctl -u myflaskapp -n 50 # 常见错误ImportError模块找不到、PermissionErrorsocket 目录无权写、SyntaxErroruwsgi.ini 语法错检查 socket 文件是否存在且权限正确ls -l /run/uwsgi.sock # 如果不存在uWSGI 没启动成功或启动后崩溃了 # 如果存在但权限不是 664sudo chmod 664 /run/uwsgi.sock 临时修复但根源在 uwsgi.ini 的 chmod-socket # 如果属主不是 flaskuser:www-datasudo chown flaskuser:www-data /run/uwsgi.sock。检查 Nginx 配置中 uwsgi_pass 路径sudo nginx -T | grep -A 5 location / # 确认 uwsgi_pass unix:/run/uwsgi.sock; 路径与 uWSGI 的 socket 参数完全一致包括大小写和斜杠。检查 SELinux/AppArmorUbuntu 16.04 默认禁用但若启用则需放行sudo aa-status # 查看 AppArmor 状态 # 如果 enabled临时禁用测试sudo systemctl stop apparmor # 若禁用后 502 消失则需为 uWSGI 和 Nginx 写 profile。5.2 500 Internal Server ErrorFlask 层的崩溃现象Nginx 返回 500但error.log里只有upstream prematurely closed connectionaccess.log显示500uWSGI 日志里却没报错。真相Flask 应用在处理请求时抛出了未捕获异常uWSGI 捕获后返回 500但默认不将 Python traceback 写入日志太敏感。解决方法在uwsgi.ini中添加log-date true log-4xx true log-5xx true # 关键开启 Python traceback 日志 py-tracebacker /var/log/uwsgi/traceback.log在 Flask 应用中全局捕获异常并记录app.errorhandler(Exception) def handle_exception(e): app.logger.exception(Unhandled Exception: %s, e) return Internal Server Error, 500然后sudo tail -f /var/log/uwsgi/traceback.log就能看到完整的 Python traceback。5.3 静态文件 404Nginx 配置陷阱现象CSS/JS 图片全部 404但 Flask 路由正常。典型错误配置# ❌ 错误location /static/ 的斜杠多余且 alias 路径末尾多了斜杠 location /static/ { alias /var/www/myflaskapp/myflaskapp/static/; } # 请求 /static/css/app.cssNginx 会去查找 /var/www/myflaskapp/myflaskapp/static//css/app.css双斜杠 #