ByteScope

405

HTTP 405 Method Not Allowed の直し方

4xx クライアントエラーIETF 標準RFC 9110

パスは存在しますし、サーバーはそのメソッド自体も知っています。ただ、ここではそのメソッドを受け付けない——そして受け付けるメソッドの一覧を渡すことが義務づけられています。

405 が出ている理由と、やるべきこと

同じ 3 桁でも、リクエストのどちら側にいるかで別々の問題になります。自分に当てはまるブロックを読んでください。

curl で 405 を再現する

失敗した URL に対してそのまま実行してください。エラーページを表示せずにステータスだけを出すので、ブラウザが描画したものではなくサーバーが言ったことが見えます。

curl
curl -sS -X POST -o /dev/null -D - -w '\n%{http_code}\n' https://example.com/

`-X POST` は curl が本来使うメソッドを置き換え、`-o /dev/null` はエラーページを捨て、`-D -` が応答ヘッダーを落とします。答えはそこにあります。探すヘッダーは `allow` です。§15.5.6 が 405 に必須としていて、そのパスが本当に受け付けるメソッドを列挙するので、1 回実行すれば呼び方が悪いのかルートが悪いのかが分かります。無いこと自体も発見として読んでください。`Allow` の無い 405 は仕様どおりに答えていないサーバーですし、`Allow` があって値が空なら、そのリソースは何も受け付けないという合法な表明です。数秒を足す価値のある変種が 2 つあります。`curl -I` は GET ではなく HEAD を送るので、`-I` で得た 405 は HEAD だけの話で、本当に気にしているメソッドの話ではないかもしれません。§9.1 に照らせば、GET が通るのに HEAD で 405 が出るのはサーバーの欠陥です。そしてブラウザのプリフライトを推測せずに再現するには `curl -sS -D - -o /dev/null -X OPTIONS -H 'origin: https://app.example.com' -H 'access-control-request-method: POST' https://example.com/api/` を実行します。ここで出る 405 が、それを一言も名指ししない CORS メッセージの本当の原因です。

405 が実際に意味していること

RFC 9110 §15.5.6 は 405 を、リクエスト行のメソッドをオリジンサーバーは知っているが、対象リソースがそれをサポートしていない状態と定義し、役に立つほうの半分を必須にしています。405 を生成するオリジンサーバーは、その対象リソースが現在サポートしているメソッドを並べた `Allow` ヘッダーフィールドを生成しなければならない (MUST)、と。`Allow` の定義は §10.2.1 にあり、読み違える前に知っておくべき細部が 1 つあります。`Allow` の値が空であることは合法で、それはリソースがどのメソッドもサポートしないという意味です。ヘッダーが存在しないことと同じではありません。意味の輪郭は隣の 2 つのコードが決めています。§15.6.2 は 501 (Not Implemented) を、どのリソースに対してもサーバーがサポートしていないメソッド用に取ってあるので、405 のほうが狭い主張です——このメソッド自体は知っている、ただしここでは駄目、ということ。そして §9.1 は、汎用のサーバーがすべて GET と HEAD をサポートすることを求めています。だから GET が通るリソースへの HEAD に 405 を返すのはポリシーではなく欠陥です。これは覚えておく価値があります。`curl -I` は HEAD を送るので、最初に手が伸びる道具が、調査対象のコードそのものを作り出しかねません。最後の性質はデプロイより長生きします。405 は RFC 9110 §15.1 がヒューリスティックにキャッシュ可能と挙げるステータスコードの一つで、404・410・501 と同じ並びにいます。つまり `Cache-Control` が一切無くてもキャッシュや CDN が保存してかまいません。まずいロールアウトの 10 分間だけ 405 を返していたルートが、ロールアウトを直したあともエッジから 405 を返し続ける——保存された応答が期限切れになるか誰かがパージするまで。404 と同じ罠で、理由も同じです。

405 と間違えられやすいコード

紛らわしいコード見分け方
501501 (Not Implemented, §15.6.2) は、どのリソースに対してもサーバーがそのメソッドをサポートしていないという意味です。405 はメソッド自体はサポートしているが、このリソースでは受け付けないという意味です。実務上の差は次に向かう先です。プロキシや古いサーバーから返る `PATCH` の 501 は迂回すべき機能の欠落、自分のアプリケーションから返る `PATCH` の 405 は登録し忘れたルートです。どちらも §15.1 でヒューリスティックにキャッシュ可能なので、どちらも原因より長生きしえます。
404一致しないメソッドに 404 を返すルーターは、答えの有用なほうの半分を隠しています。§15.5.6 がその区別を重要にしている理由がこれです。405 には `Allow` が付き、404 には何も付きません。判定はコマンド 1 本です。あるパスが POST には 404、GET には 200 を返すなら、それは番号を間違えて着ている 405 であって、見るべきはパスではなくメソッドです。
301いま見ている 405 は、呼び方ではなくリダイレクトの副産物かもしれません。§15.4.2 は、ユーザーエージェントが 301 を辿るとき歴史的に POST を GET へ書き換えてきたと記録していますし、実際には 302 でも同じことが起きます。だから `http://` から `https://` へ、あるいは末尾スラッシュを足すリダイレクトを跨いだ POST は、POST しか受け付けないルートに GET として到着しえます。メソッドを保つリダイレクトは 307 と 308 で、API がそちらを使うべき理由です。
CORS preflightJSON のボディやカスタムヘッダーを持つオリジン跨ぎのリクエストの前には、ブラウザ自身が生成する `OPTIONS` が飛びます。OPTIONS のルートが無いサーバーはそれに 405 で答えます。するとブラウザはプリフライトが成功しなかったとして CORS の失敗を報告するので、メッセージは間違った問題を名指しします。本当の原因は、失敗したリクエストの 1 行上にあるネットワークパネルの 405 です。
Allow§10.2.1 は `Allow` を、対象リソースがサポートするメソッドの一覧と定義し、§15.5.6 は 405 でそれを送ることを必須にしています。だから無いのはヒントではなくサーバーのバグです。値が空の場合は注意して読んでください。空の `Allow` は「このリソースはどのメソッドもサポートしない」という合法な言い方で、ヘッダーが存在しないのとは別の主張です。
理由句
Method Not Allowed
クラス
4xx クライアントエラー
定義
RFC 9110
標準
IETF 標準

このサイトのツール

どれもブラウザ内で完結します。アップロードは発生しません。

関連するステータスコード

405 を調べているとき、ついでに読むことになりがちなコードです。

よくある質問

このステータスコードを出したのがどのサーバーかを知るには?

ページではなく応答ヘッダーを読んでください。curl -sS -o /dev/null -D - https://example.com/path が本文なしでヘッダーだけを出し、serverviacf-ray の 3 つで、答えたのがどの層かが分かります。cf-ray の値があれば応答を扱ったのは Cloudflare で、その値はサポートが聞いてくる ID です。エッジを完全に外して確かめたいなら、--resolve example.com:443:203.0.113.10 を付けて同じリクエストを投げ直します。元のホスト名と SNI を送りつつ、指定したオリジンのアドレスへ接続するので、答えが変われば、エッジとオリジンの言い分が食い違っているということです。

数字のあとの理由句(Not Found など)に意味はありますか?

ありません。分岐条件にしてはいけません。RFC 9112 はクライアントに理由句を無視するよう求めていますし、サーバーは自由に変えられますし、HTTP/2 と HTTP/3 にはそもそも理由句が存在しません。HTTP/1.1 では 404 Not Found として届くものが、HTTP/2 では素の 404 として届きます。このサイトが登録済みの理由句を載せているのは、それが検索される語であり HTTP/1.1 のログに出る文字列だからで、何かのソフトウェアがそれに依存しているからではありません。

このコードで失敗したリクエストは再試行すべきですか?

5xx と 429 は再試行してよく、4xx はしないでください。次に投げても中身は同じだからです。応答に Retry-After が付いていればそれに従ってください。RFC 9110 がまさにこのために定義していて、値は秒数でも日時でも構いません。付いていなければ、ジッター付きの指数バックオフと明確な上限を使い、しかも冪等なメソッドに限ってください。再試行された POST はカードに二重で課金しかねません。すでに失敗しているサーバーへの再試行の嵐は、短い障害を長い障害に変える典型的な経路です。

自分のサーバーが返すステータスコードは変えられますか?

変えられます。守るべき規則は 1 つだけ、そのコードが本当であること。応答を自動で読むもの——検索エンジン、監視、キャッシュ、クライアント側の再試行ロジック——はすべて数字だけで判断し、ページの中身は一切見ません。エラーページを 200 で返せば自分の監視から障害が隠れますし、無いページを 200 で返せばエラーが本文として索引されますし、単に不正なリクエストに 500 を返せば、当番の人をスタックの間違った半分へ送り込むことになります。

405 でまだ詰まっているなら、ステータスコード一覧をすべて見る。あるいは上に戻って、自分の立ち位置に向けて書かれたブロックを読んでください。