@app.errorhandler(404) 不生效最常见原因是注册时机错误:须在应用实例创建后、app.run()前注册,且不能仅在条件分支(如if debug)中注册;若用蓝本,需用@app.errorhandler而非@bp.errorhandler。

为什么 @app.errorhandler(404) 不生效?
最常见原因是注册时机不对:Flask 要求错误处理器在应用实例创建后、app.run() 之前注册,且不能放在蓝本(Blueprint)里却没正确注册到主应用。如果用工厂模式(create_app),函数必须在 app 对象返回前完成注册。
- 确保不是在某个条件分支里才注册——比如只在
if DEBUG:下写了@app.errorhandler(404),生产环境就失效 - 若用了蓝本,
@bp.errorhandler(404)只对蓝本路由内的 404 生效;全局 404 必须用@app.errorhandler(404) - 检查是否误写了
@app.errorhandler(“404”)(字符串)——必须是整数404
怎么让 404 页面返回 HTML 而不是纯文本?
默认 Flask 的 @app.errorhandler(404) 返回的是 text/plain 响应体,哪怕你 render_template 了,也可能因缺少明确 MIME 类型被浏览器当文本显示。关键在于返回值类型和响应头。
- 直接返回
render_template("404.html")即可——Flask 自动设为text/html,前提是模板存在且路径正确 - 如果手动构造响应,务必用
make_response(render_template(...))或显式指定mimetype="text/html" - 避免返回字符串如
"Page not found",这会触发默认纯文本响应 - 模板中不要依赖未定义的上下文变量(比如
{{ user.name }}),否则 404 页自己抛 500
@app.errorhandler(404) 和 app.register_error_handler(404, ...) 有啥区别?
本质一样,只是写法不同:@app.errorhandler(404) 是装饰器语法糖,app.register_error_handler(404, func) 是运行时注册方式。但后者更灵活,适合动态加载或测试场景。
- 装饰器写法简洁,适合固定逻辑;register 方式可用于条件注册,比如只在测试环境启用自定义 404
- 两者注册的函数签名必须一致:
def not_found(e):,参数e是NotFound异常实例(来自 Werkzeug),可从中取e.description或e.name - 注意:重复注册同个状态码会覆盖前一个,不会合并
静态文件 404 为什么绕过 @app.errorhandler(404)?
Flask 默认静态文件由开发服务器直接服务(/static/ 路径),不经过路由系统,所以找不到时直接返回默认 404,不触发你的处理器。这是开发模式行为,生产部署(如 Nginx + Gunicorn)通常由反向代理处理静态文件,同样绕过 Flask。
- 开发时想统一处理,可禁用自动静态路由:
app = Flask(__name__, static_folder=None),然后手动加路由模拟(不推荐用于生产) - 更实际的做法:确保前端构建工具生成的静态资源路径准确,404 日志里看到大量
/static/js/app.xxx.js找不到,大概率是构建配置或 CDN 缓存问题 - 真正需要拦截静态 404 的场景极少,优先查构建输出和 Nginx 配置,而不是强行塞进 Flask 错误处理器
最易忽略的一点:404 处理器函数里别再 raise 新异常,也别调用可能出错的外部服务(比如查数据库记录访问日志),否则用户看到的可能是 500 而不是你精心设计的 404 页面。










