
Gunicorn 返回 404 的根本原因是 Flask 应用实例未正确初始化路由——create_app() 调用被包裹在 if __name__ == '__main__': 内,导致 Gunicorn 加载 app:app 时仅获得未配置的原始 Flask() 实例,路由未注册。
gunicorn 返回 404 的根本原因是 flask 应用实例未正确初始化路由——create_app() 调用被包裹在 if __name__ == '__main__': 内,导致 gunicorn 加载 app:app 时仅获得未配置的原始 flask() 实例,路由未注册。
在使用 Gunicorn 部署 Flask 应用时,一个常见却隐蔽的错误是:应用工厂函数(如 create_app())未被实际调用,导致 WSGI 服务器加载的只是一个空的、未注册任何路由的 Flask 实例。这正是你遇到 gunicorn -b 0.0.0.0:8000 app:app 成功启动却始终返回 404 的核心原因。
? 问题根源分析
你的原始代码中:
app = Flask(__name__, static_folder='ui')
def create_app(config_name='default'):
CORS(app)
app.config.from_object(config[config_name])
return app # ⚠️ 注意:此函数虽定义,但未被调用!
@app.route('/')
def index(path=''):
# ... 路由逻辑
pass
if __name__ == '__main__':
app = create_app(os.getenv('FLASK_CONFIG', 'default')) # ✅ 仅在此处调用
app.run()
Gunicorn 通过 app:app 导入模块时,执行的是模块顶层代码。此时:
-
app = Flask(...)创建了一个裸 Flask 实例; -
@app.route(...)尝试向该实例注册路由 —— 但此时create_app()尚未执行,CORS、配置、可能的蓝图注册等均未生效; - 更关键的是:若
create_app()中还包含app.register_blueprint(...)或其他路由增强逻辑,这些将完全失效; - 最终 Gunicorn 持有的
app对象不含有效路由,所有请求(包括/)均匹配失败 → 404。
而 python app.py 能工作,是因为 if __name__ == '__main__': 块被执行,create_app() 被调用并覆盖了 app 变量,此时 app.run() 运行的是已完整初始化的实例。
✅ 正确写法:确保 create_app() 在模块顶层执行
将应用工厂调用移至模块顶层,并显式返回配置后的应用实例:
from flask import Flask, send_from_directory
from flask_cors import CORS
import os
# 1. 不再提前创建 app 实例
# app = Flask(...) ← 删除这一行!
def create_app(config_name='default'):
"""应用工厂:返回已配置并注册路由的 Flask 实例"""
app = Flask(__name__, static_folder='ui')
CORS(app)
# 假设 config 是预定义的字典,例如:config = {'default': DevelopmentConfig}
# app.config.from_object(config[config_name])
# 2. ✅ 关键:在此注册所有路由(必须在 create_app 内部)
@app.route('/', defaults={'path': ''})
@app.route('/<path>')
def index(path):
if path and os.path.exists(os.path.join(app.static_folder, path)):
return send_from_directory(app.static_folder, path)
return send_from_directory(app.static_folder, 'index.html')
return app
# 3. ✅ 在模块顶层调用工厂函数,生成最终的 WSGI 可用实例
app = create_app(os.getenv('FLASK_CONFIG', 'default'))</path>
然后使用标准命令启动:
gunicorn -b 0.0.0.0:8000 app:app
? 为什么这样可行?
Gunicorn 加载app:app时,会导入app.py模块 → 执行create_app()→ 注册路由 → 返回配置完成的app实例。该实例即为 WSGI 入口,具备全部路由能力。
⚠️ 注意事项与最佳实践
-
不要在工厂外定义
app = Flask(...):避免产生“幽灵实例”,干扰路由注册; -
路由必须在
create_app()内部注册:若使用蓝图,也应在此处app.register_blueprint(bp); -
静态文件路径需健壮:建议用
os.path.join(app.static_folder, path)替代字符串拼接,防止路径穿越风险; -
开发 vs 生产入口分离:
if __name__ == '__main__':仅用于本地调试,生产环境一律交由 Gunicorn 管理生命周期; -
验证是否生效:启动后访问
http://localhost:8000,同时检查 Gunicorn access log 是否出现200响应,而非持续404。
修复后,你的前端单页应用(SPA)将能通过 Gunicorn 正确服务 index.html 及其资源,彻底解决“启动成功却 404”的部署陷阱。










