プロキシ企業がステータスコードについて記事を書く理由:私たちは Geonode であり、ユーザーはAPIトラフィックを私たちを経由して送信するため、特定のエラーが「プロキシに起因するもの」かどうかという問い合わせを受けます。415の場合、その答えは基本的に常に「いいえ」です。 415はオリジンサーバーから返されるもので、ユーザーが構築したリクエストに関する何らかの情報を表しています。 中継サーバーがこれを生成する可能性はごく限られた状況下(ボディを検査するフィルタリングプロキシや、独自のコンテンツルールを持つゲートウェイなど)にありますが、それは稀なケースであり、その場合はレスポンスヘッダーにその旨が明示されます。 プロキシ経由で415エラーが発生している場合は、プロキシを介さずに直接リクエストを送信すれば、ほぼ間違いなく同じ415エラーが返ってくるはずです。リクエストを修正してください。プロキシに起因する唯一のステータスコードは407であり、その名前からもそれがわかります。
さて、実際のエラーについてです。
仕様書の記載内容
RFC 9110の第15.5.16節では、次のように明確に定義されています:
ステータスコード 415 (Unsupported Media Type) は、コンテンツの形式が対象リソースにおけるこのメソッドでサポートされていないため、オリジンサーバーがリクエストの処理を拒否していることを示します。
この文のうち、3つの部分が正しいです。
「コンテンツ」 — リクエスト本文を指し、URLでもクエリ文字列でもレスポンスでもありません。リクエストに本文が含まれていない場合、415が返されるのは異例であり、何か別の問題が発生していることを示唆します。
「このメソッドではサポートされていない」 — サポートはメソッドごとに異なります。あるリソースはPOSTではapplication/jsonを受け入れ、PATCHでは拒否する場合があり、同じエンドポイントが異なる動詞(verb)の下で異なる挙動を示す場合、これは実に一般的な混乱の原因となります。
「対象のリソースにおいて」 — かつリソースごとに異なります。API 上の 1 つのエンドポイントが特定の形式を受け入れたとしても、別のエンドポイントについては何も示唆しません。
仕様書では、その原因として以下を挙げています:
形式の問題は、リクエストで指定された Content-Type や Content-Encoding に起因する場合もあれば、データを直接検査した結果である場合もあります。
この最後の文言は重要ですが、広く見過ごされています。サーバーは、単にヘッダーを読み取った後だけでなく、バイトデータを検査した後に415を返すことも許可されています。Content-Type: application/jsonを宣言して、JSONではないデータを送信した場合、400ではなく415が返されることは正当な動作です。
RFCでは、サーバーがどのような情報を通知すべきかについても規定されています。問題がコンテンツエンコーディングにある場合、Accept-Encodingレスポンスヘッダーを「どのコンテンツエンコーディング(もしあれば)が受け入れられたかを示すために使用すべきである」とされています。メディアタイプに問題がある場合は、Acceptを「どのメディアタイプが受け入れられたかを示すために使用できる」とされています。 実際には、MDNのドキュメントによると、サーバーはメソッド固有のケースにおいてAccept-PostやAccept-Patchを一般的に使用しており、これはさらに有用です。
レスポンスヘッダーをよく読みましょう。サーバーは頻繁に答えを伝えていますが、クライアントはそれを無視しがちです。
原因となる6つの要因
発生頻度の高い順に概ね並べています。
1. Content-Type が完全に欠落している。 ボディを送信する際に、そのフォーマットを宣言していません。多くのフレームワークはこれを推測しません。MDNの例がまさにこれです:JSONボディとContent-Lengthを含むPOSTリクエストで、Content-Typeが指定されていない場合、415やAccept-Post: application/json; charset=UTF-8といったレスポンスが返されます。
2. 誤ったContent-Type。 典型的な例は、application/x-www-form-urlencodedと宣言しながらJSONを送信してしまうケースです。これは通常、HTTPクライアントがデフォルトでフォームエンコーディングを使用しており、JSON文字列を変更せずにそのまま渡してしまったことが原因です。本文自体は問題ありませんが、ラベルが間違っています。
3. 似ているが間違ったメディアタイプ。 application/jsonの代わりにtext/jsonを送信してしまうケース。サーバーがtext/xmlを求めているのに、application/xmlを送信してしまうケース。 application/vnd.api+json のようなベンダー固有のメディアタイプが指定されているのに、application/json をプレーンテキストで送信してしまった場合などです。厳格なサーバーは完全一致をチェックするため、例外は認められません。
4. 文字コードの問題。 MDN が最も明確な例を挙げています。サーバーが UTF-8 を要求しているのに、UTF8 を送信してしまう場合です。登録された名称におけるハイフンは省略不可であり、厳格なパラメータ検証を行うサーバーがこれを拒否するのは正当な行為です。
5. サーバーがサポートしていないContent-Encoding。 リクエストボディをgzip圧縮し、Content-Encoding: gzipを設定したにもかかわらず、サーバーがアイデンティティエンコーディングしか処理できない場合です。RFCはこのケースを具体的に想定しており、適切に動作するサーバーであれば、Accept-Encodingを返して、どのような形式を受け入れるかを通知するはずです。
6. ボディが宣言された型と一致しない。 ヘッダーは正しいがバイト内容が間違っている — 多くの場合、シリアライゼーションのバグ、空の文字列を出力したテンプレート、あるいはスタック内のどこかでボディが二重にエンコードされてしまったことが原因です。これは「データを直接検査する」という条項が適用されるケースです。
415 対 406 対 400 対 422
これが混乱の原因となっている比較表ですが、仕様書ではこれらを明確に区別しています。
| コード | 意味 | 方向 | 修正方法 |
|---|
| 415 | 送信されたフォーマットがサポートされていません | リクエスト本文 | Content-Type または Content-Encoding |
| 406 | 受け入れ可能な表現が利用できません | レスポンス | Accept ヘッダー |
| 400 | リクエストの形式が不正です | リクエスト全体 | 構文またはフレーム |
| 422 | フォーマットは理解できるが、コンテンツを処理できない | リクエスト本文 | データ自体 |
| 413 | 本文が大きすぎる | リクエスト本文 | ペイロードサイズ |
415 と 406 の違いは方向性の問題であり、一度説明されれば最も理解しやすいものです。 415は送信した内容に関するものです。406は受信を要求した内容に関するものです。RFC 9110では、406を「受信したプロアクティブネゴシエーションヘッダーフィールドに基づき、ユーザーエージェントが受け入れ可能な現在の表現をリソースが持っていない」状態と定義しています。 406が返ってきた場合は、ボディではなくAcceptヘッダーを確認してください。
415と400の違い。 RFC 9110では、400について、サーバーが「クライアント側のエラーとみなされる事由(例:リクエスト構文の不正、リクエストメッセージのフレーム構成の無効、または欺瞞的なリクエストルーティング)」によりリクエストを処理しない状態であると説明しています。 400は構造的な問題であり、リクエスト自体が不正です。415は、リクエスト自体は正しく構成されているものの、その本文の形式がサーバーで受け入れられない場合です。 実際には、415の方がより正確であるにもかかわらず、多くのサーバーが400を返すことがあります。これは制御できないため、ボディを含むリクエストに対して400が返された場合は、それが実質的に415である可能性があるとみなしてください。
415と422の違いは、RFCが最も明確に区別している点です。 セクション15.5.21によると、422は「サーバーがリクエストコンテンツのコンテンツタイプを理解しており(したがって415(Unsupported Media Type)ステータスコードは不適切である)、リクエストコンテンツの構文は正しいが、含まれる指示を処理できなかった」ことを示します。
つまり、優先順位は次の通りです。415はラッパーが間違っていることを意味し、422はラッパーは正しく、内容が間違っていることを意味します。必須フィールドが欠落している、構文的に正しいJSONは422となります。同じJSONがフォームデータとして指定された場合は415となります。
2分以内で原因を特定する
ほぼすべてのケースを解決できる決まった手順。
ステップ1:レスポンスヘッダーを確認する。 ステータス行ではなく、ヘッダーです。
curl -i -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
レスポンス内に「Accept」、「Accept-Post」、「Accept-Patch」、または「Accept-Encoding」が含まれていないか確認してください。いずれかが存在する場合、それはサーバーが求めていることを直接示しているものであり、これで解決です。
ステップ2:実際に送信した内容を確認する。 送信しようとした内容ではなく、実際にネットワーク上を流れた内容です。 クライアントライブラリはヘッダーを追加、上書き、再フォーマットするため、コードで設定したヘッダーが必ずしも実際に送信されたヘッダーとは限りません。
curl -v -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}' 2>&1 | grep '^>'
> の行が、実際のリクエストです。415エラーの驚くほど多くの割合が、まさにここで解決されます。つまり、慎重に設定した Content-Type が、デフォルト値によって上書きされていたことが判明するのです。
ステップ3:メソッドを確認する。 同じエンドポイントでも、POSTではタイプを受け入れ、PATCHでは拒否する場合があります。同じボディを別の動詞で試してみて、挙動が変わるかどうかを確認してください。
ステップ4:タイプの文字列を正確に確認する。 ドキュメントと1文字ずつ照合してください。application/json 対 text/json。UTF-8 対 UTF8。 ベンダー固有のサフィックス。これは面倒な作業ですが、答えが見つかることが多い箇所です。
ステップ5:その特定のエンドポイントに関するドキュメントを読む。 APIは内部的に統一されていません。JSON APIであるにもかかわらず、ファイルアップロードのエンドポイントで multipart/form-data を要求するのは、まったく珍しいことではありません。
クライアント側での修正
一般的なクライアントにおけるよくあるケース。
curl. -d は、特に指定がない限り application/x-www-form-urlencoded を意味します。これが、コマンドラインから 415 エラーが発生する最も一般的な原因です:
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
JavaScript の fetch。 文字列のボディを渡すと、Content-Type がまったく設定されません:
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "test" }),
});
知っておくべき例外:FormDataを使用する場合、Content-Typeを自分で設定してはいけません。このヘッダーにはマルチパートの境界が含まれているため、ブラウザが自動的に生成する必要があります。これを上書きするとリクエストが破損し、多くの場合415エラーとして現れます。
Pythonのrequests。 data=ではなくjson=を使用すれば、ヘッダーは自動的に設定されます:
requests.post(url, json={"name": "test"}) # application/json
requests.post(url, data={"name": "test"}) # form-encoded
Axios。 プレーンなオブジェクトに対しては application/json を設定し、文字列に対しては別の設定を行うため、これが予期せぬ結果を引き起こす原因となることがよくあります。ペイロードをすでにシリアライズしている場合は、ヘッダーを明示的に設定してください。ここでのクライアント間の動作の違いは、まさに axios vs fetch で取り上げたようなものです。
一般的なルール: クライアントがJSON専用のパラメータを提供している場合は、手動でシリアライズしてデフォルトのヘッダーが正しいことを期待するよりも、そのパラメータを使用してください。
サーバー側での適切な応答
APIを呼び出す側としては、以下の点に気を配ることで、APIの利用が格段に容易になります。
415ステータスコードと共に、Accept-PostまたはAccept-Patchヘッダーを送信してください。 RFCではAcceptまたはAccept-Encodingが推奨されていますが、メソッド固有のバリエーションの方がより正確であり、MDNでもまさにこの用途について記載されています。 この単一のヘッダーだけで、デバッグセッションを一目瞭然のものに変えることができます。
読みやすいボディを含めてください。 ボディが空のステータスコードは、クライアントに推測を強いることになります。実際に受け取った内容と期待していた内容を明記してください。
415と422を正しく区別してください。 コンテンツタイプを認識し、ボディの解析には成功したが検証に失敗した場合は、422を返すべきです。検証失敗に対して415を返すと、ヘッダーに問題がないにもかかわらず、ユーザーにヘッダーの確認を強いることになり、これはAPI設計においてよくある、かつ大きな損失をもたらすミスです。
安全に許容できる場合は、パラメータに対して寛容であるようにしてください。 application/jsonを受け入れる一方で、application/json; charset=utf-8を拒否することは、技術的には正当化できるものの、実用上は役に立ちません。メディアタイプを適切に解析し、不要なパラメータは無視してください。
415を一般的な拒否コードとして使用しないでください。 415には特定の意味があります。このステータスコードを多義的に使用すると、APIの使い勝手が悪くなり、クライアント側の再試行ロジックが誤ったものになってしまいます。
415が実際には415ではない場合
ステータスコードが誤解を招くケース。
ゲートウェイやWAFがリクエストを拒否した。 一部のセキュリティ層では、実際のコンテンツタイプに関係なく、不審と判断したリクエスト本文に対して415を返すことがあります。その手がかりとなるのは、通常、アプリケーションから送信されたものには見えないレスポンス本文や、仲介者を特定するヘッダーです。
コードが実行される前に、フレームワークのデフォルト処理が実行された。 多くのWebフレームワークは、ミドルウェア内で未知のコンテンツタイプを拒否します。ハンドラが実行されていないため、アプリケーションロジックのどの部分も修正には関係ありません。
ロードバランサーやCDNがヘッダーを削除した。 稀ですが実際に起こり得ます。直接送信した場合はリクエストが成功するのに、インフラを経由すると失敗する場合は、アプリケーションが変更されたと決めつける前に、両端のヘッダーを比較してください。
エンドポイントが存在しません。 ルーティングが解決される前にコンテンツタイプのネゴシエーションが行われるため、一部のサーバーはPOSTリクエストで一致しないルートに対して404ではなく415を返します。URLを確認してください。
HTTPメソッドのオーバーライドが失敗した。 フレームワークがヘッダーやクエリパラメータによるメソッドのオーバーライドをサポートしている場合、実際に適用されるメソッドは送信したものと異なる可能性があり、コンテンツタイプのサポートはメソッドごとに異なります。
これらのいずれの場合も、解決策はペイロードの上流にあります。一般的な原則として、リクエストが明らかに正しく、それでも415エラーが続く場合は、ボディの編集を中止し、パス内のどのコンポーネントがレスポンスを生成しているのかを特定し始めましょう。
よくある質問
「415 Unsupported Media Type」とはどういう意味ですか?
リクエスト本文の形式が、そのリソースのそのメソッドではサポートされていないため、サーバーがリクエストを拒否しました。RFC 9110 によると、このエラーは Content-Type ヘッダー、Content-Encoding ヘッダー、またはサーバーが本文を直接検査したことに起因するとされています。 これは、送信したデータの形式に関するものであり、データの正誤性に関するものではありません。
415エラーを修正するにはどうすればよいですか?
まずレスポンスヘッダーを確認してください。サーバーは、Accept、Accept-Post、またはAccept-Encodingといったヘッダーを返して、必要な形式を正確に指定することがよくあります。その後、ライブラリがヘッダーを上書きしてしまうことがあるため、クライアントが実際に送信した内容を確認してください。 最も一般的な解決策は、Content-Type: application/jsonが指定されていなかったリクエストにこれを追加することです。
415と400の違いは何ですか?
400は、リクエストの形式が不正であることを意味します(構文やフレーム構成に問題がある場合)。415は、リクエストの形式は正しいものの、本文の形式がサポートされていないことを意味します。 実際には、415の方がより正確であるにもかかわらず、サーバーが400を返すことが多いため、ボディを含むリクエストで400が返された場合は、コンテンツタイプの問題の可能性として調査する価値があります。
415と422の違いは何ですか?
RFC 9110では、この違いが明確に定義されています。422は、サーバーがコンテンツタイプを認識し、構文も正しかったものの、指示を処理できなかったことを意味します。つまり、415は「ラッパー(外側の構造)」に問題があるのに対し、422は「内容」そのものに問題があることを示します。必須フィールドが欠落している有効なJSONの場合、422が返されます。
ファイルをアップロードする際に 415 エラーが発生するのはなぜですか?
通常、マルチパートリクエストにおいて Content-Type が手動で設定されたことが原因です。このヘッダーにはマルチパートの境界が含まれているため、ブラウザやクライアントが自動的に生成する必要があります。手動で設定すると境界が削除され、サーバーが解析できないリクエストが生成されてしまいます。
プロキシが 415 エラーの原因になることはありますか?
めったにありません。415はオリジンサーバーから返されるもので、リクエスト本文に関するエラーです。コンテンツを検査するフィルタリングプロキシやゲートウェイがこれを発生させる可能性はありますが、プロキシ特有の状態コードとしては通常407が使用され、その名前からもそれがわかります。プロキシを介さずにテストしてみてください。それでも415が続く場合は、プロキシは原因ではありません。
415は、JSONが無効であることを意味しますか?
必ずしもそうとは限りません。サーバーがコンテンツタイプを拒否した場合、JSONは検査されていません。サーバーが正しいタイプを宣言した後、ボディが実際にはその形式ではないと判明した場合、その通りです。RFCでは、「データを直接検査した」後に拒否することを認めています。 まずはヘッダーに関する質問を確認してください。そちらの方がはるかに一般的な原因です。
415エラーが発生した後、再試行すべきですか?
いいえ。これはクライアント側のエラーであり、同じリクエストを送信しても同じ結果になります。再試行はリクエストの無駄になるだけでなく、レート制限を受けている場合は状況を悪化させる可能性があります。コンテンツタイプを修正して、一度だけ送信してください。
まとめ
415 の意味は限定的かつ明確です。つまり、データに付加されたラッパーが、このエンドポイントがこのメソッドに対して受け入れる形式ではないということです。これは、データが無効であるということ(それについては 422 が用いられます)でもなければ、要求したレスポンスの内容に関する問題(それについては 406 が用いられます)でもありません。
意味が限定的であるため、原因の特定も簡単です。レスポンスヘッダーを確認してください。適切に動作するサーバーであれば、Accept、Accept-Post、またはAccept-Encodingに受け入れ可能なタイプが明記されています。また、デフォルト設定やミドルウェアによって設定したヘッダーが上書きされることが多いため、クライアントに指示した内容ではなく、実際にネットワーク上に送信された内容を確認してください。 これら2つの手順を踏むだけで、ボディに一切手を加えることなく、ほとんどのケースを解決できるはずです。
また、リクエストに問題がないように見えても415エラーが続く場合は、レスポンスが想定しているアプリケーションから返されているわけではない可能性が高いです。 ゲートウェイ、フレームワークのミドルウェア、および一致しないルートはすべて、ペイロードとは無関係に415エラーを発生させます。その時点で重要なのは、「何を変更すべきか」ではなく、「パス内のどのコンポーネントが応答しているのか」を特定することです。