エラーの内容
フォーム、fetch()、または curl から POST リクエストを送信すると、Nginx が次のエラーを返します:
405 Method Not Allowed
レスポンスヘッダーには以下が含まれます:
Allow: GET, HEAD
バックエンドにはリクエストが届きません。Nginx が先に遮断してしまいます。
根本原因
Nginx の静的ファイルモジュール(ngx_http_static_module)は GET と HEAD のみを処理します。POST リクエストがディスク上のファイルに解決される location ブロックに到達すると、Nginx は即座に 405 で拒否します。アプリにはリクエストが届きません。これは root、alias、try_files のいずれでも発生します。
このエラーを引き起こす 4 つのパターン:
action="/submit"を持つフォームが、静的なrootブロックにマッチする URL を指している- React や Vue の SPA で、
try_files $uri /index.htmlがバックエンドへの API 呼び出しを含むすべてのルートをキャッチしている - location ブロックの順序:静的なキャッチオール
location /が、より具体的なプロキシブロックより先にマッチする /api/loginのような API エンドポイントが誤って静的ファイルとして配信されている
まず診断する
変更を加える前に、リクエストを処理している location ブロックを特定します:
sudo nginx -T | grep -A 10 "location"
次に curl でテストし、返ってくる内容を確認します:
curl -sv -X POST https://yourdomain.com/api/submit \
-H "Content-Type: application/json" \
-d '{"key":"value"}' 2>&1 | grep -E "&1 | grep X-Debug-Upstream
- IP アドレス付きのヘッダーが存在する:405 はアプリサーバーから返されています。Nginx ではなく、アプリ側で修正してください。
- ヘッダーが存在しない:Nginx 自身が 405 を生成しています。修正 1 または修正 2 を適用してください。
修正を確認する
設定を編集したら、構文チェックをしてリロードします:
# 構文チェック
sudo nginx -t
# ダウンタイムなしでリロード
sudo nginx -s reload
失敗していたリクエストを再テストします:
curl -sv -X POST https://yourdomain.com/api/submit \
-H "Content-Type: application/json" \
-d '{"test": true}'
修正が成功すると、実際のバックエンドレスポンス(200、201、またはアプリからのバリデーションエラーなど)が返ってきます。405 以外であれば何でも構いません。まだ 405 が表示される場合は、編集した設定ファイルが実際に読み込まれているか確認してください:
sudo nginx -T | grep "include"
予防策
- **常に具体的な location ブロックを汎用的なものより前に定義してください。**静的ファイルとバックエンドプロキシを混在させるすべてのサーバーブロックで、
/api/を/より上に配置します。 - **
try_filesと API プロキシは別々のブロックに保ちます。**SPA フォールバック用に 1 つのブロック、プロキシ用に別のブロック — 例外なしです。 - **ステージング環境ですべての HTTP メソッドをテストします。**デプロイのたびにワンライナーで即座に検出できます:
curl -X POST https://staging.example.com/api/health。 - **開発中はエラーログを監視します。**405 はブラウザで気づくより早く
/var/log/nginx/error.logに記録されます:tail -f /var/log/nginx/error.log。

