Flask で /console だけ謎のエラーになる問題
Flask アプリケーションを開発していて、なぜか /console というパスだけ正常に動作しない、という現象に遭遇することがあります。他のエンドポイントは問題ないのに、/console への POST リクエストだけが期待どおりに動かない。しかもエラーも警告も出ない。この記事では、その原因と解決策を解説します。
症状
/console エンドポイントを定義して JSON API を実装したとします。
@app.route('/console', methods=['POST'])
def console_api():
data = request.json
# 何らかの処理
return jsonify({'status': 'ok'})このエンドポイントに POST リクエストを送ると、以下のような症状が発生します。
JSON を期待しているのに、なぜか HTML が返ってくる。ステータスコードは 200 OK なので、通信自体は成功しているように見えます。
Failed to parse JSON response SyntaxError: Unexpected token '<' というエラーが発生します。HTML の < を JSON としてパースしようとして失敗しています。
さらに厄介なのは、Flask 側のログにリクエストが記録されないことです。まるで自分が書いたルートが存在しないかのように振る舞います。
原因
この問題の原因は、Werkzeug のデバッガが /console というパスを予約していることにあります。
Flask を debug=True で起動すると、内部で Werkzeug の DebuggedApplication ミドルウェアが有効になります。このミドルウェアは、例外発生時にブラウザ上でインタラクティブなデバッグコンソールを提供する機能を持っています。そして、そのコンソールのパスがデフォルトで /console に設定されているのです。
# Werkzeug のデバッガ定義(抜粋)
class DebuggedApplication:
def __init__(
self,
app,
evalex=False,
console_path='/console', # ← これが問題
...
):ミドルウェアはアプリケーションより先にリクエストを処理するため、ユーザーが定義した /console ルートに到達する前に、Werkzeug が横取りしてしまいます。その結果、デバッグコンソール用の HTML ページが返されるわけです。
Werkzeug のデバッガが /console を横取り。ユーザー定義のルートは無視される。
デバッガが無効なので /console は正常に動作。開発時だけ問題が起きる罠。
解決策
方法 1: デバッグモードを無効化する
最もシンプルな解決策は、debug=False で起動することです。
app.run(debug=False, use_reloader=True, host='127.0.0.1', port=5000)use_reloader=True を指定すれば、ファイル変更時の自動再起動機能は維持されます。デバッグモードで得られるインタラクティブなエラー画面は使えなくなりますが、/console パスは正常に動作するようになります。
方法 2: console_path を変更する
デバッグ機能を維持しつつ問題を回避するには、console_path を別のパスに変更します。
from werkzeug.debug import DebuggedApplication
# デバッガを手動で設定し、console_path を変更
app.wsgi_app = DebuggedApplication(
app.wsgi_app,
evalex=True,
console_path='/__debug_console__'
)
app.run(debug=False, host='127.0.0.1', port=5000)debug=False で起動しつつ、DebuggedApplication を手動でラップすることで、デバッグ機能と /console の両立が可能になります。
方法 3: パス名を変更する
そもそも /console という名前を避けるのも一つの選択肢です。
@app.route('/admin/console', methods=['POST'])
def console_api():
# ...ただし、これは既存の API を変更することになるため、クライアント側の修正も必要になります。
なぜこの問題は発見しにくいのか
この問題が厄介なのは、通常のデバッグ手法では原因にたどり着きにくい点にあります。
まず、エラーが発生しません。200 OK で正常にレスポンスが返ってくるため、HTTP レベルでは問題がないように見えます。次に、Flask のログに記録されません。リクエストがアプリケーションに到達していないので、ログを見ても何も分かりません。そして、他のパスでは発生しません。/console という特定のパスでのみ問題が起きるため、「自分のコードのどこかがおかしい」と思い込みやすいのです。
開発環境と本番環境で挙動が異なるため、「本番では動くのに開発環境で動かない」という逆転現象が起きるのも混乱の原因になります。
教訓
/console という一般的な名前を、警告なしに予約するのは設計上の問題と言えます。管理画面やダッシュボードで普通に使いそうなパスだからです。もし /__werkzeug_console__ のような明らかに予約された名前であれば、誰も踏まなかったでしょう。
Flask や Werkzeug を使う際は、デバッグモードが有効な状態で予約されているパスがあることを頭の片隅に置いておくと、同様の問題に遭遇したときに原因を素早く特定できます。












