HTTP ステータスコードを設計判断で迷わず選ぶための整理

約8分

HTTP ステータスコードは何百種類もあるように感じますが、実務でよく使うのは20個程度です。問題は「200 でいいのか 201 か」「401 か 403 か」のような選択で、ここで一貫した判断基準を持っているかが API 設計の品質に出ます。本記事では迷いやすい組み合わせを中心に整理します。

クラス分けの本来の意味

ステータスコードは先頭1桁で5クラスに分かれます。仕様(RFC 9110)の定義文を正確に把握するのが、迷いを減らす第一歩です。

クラス名前意味
1xxInformational中間応答(暫定的)
2xxSuccessfulリクエストが正常に受理された
3xxRedirectionクライアント側で追加アクションが必要
4xxClient Errorクライアントの誤りによりサーバーが処理を拒否した
5xxServer Errorサーバー側の障害で処理に失敗した

「クライアントの誤り」と「サーバー側の障害」の境界が曖昧になりがちで、ここを4xx か5xx かで適切に切り分けることが、運用時のアラート設計(例:5xx のみページャーを鳴らす)に直結します。

2xx:成功系の使い分け

コード用途
200 OK一般的な成功
201 Created新しいリソースの作成に成功(POST の典型)
202 Accepted受理したが処理は非同期で続く(バックグラウンドジョブ)
204 No Content成功したがレスポンスボディなし(DELETE の典型)

201 vs 200

リソースを新規作成した場合は 201 + Location ヘッダで作成された URL を返すのが REST の作法です。200 で済ませる API も多いですが、CRUD を厳密に表現するなら 201 を使う価値があります。

202 の使いどころ

「メール送信を受け付けた、実際の送信は別ジョブで」のような非同期処理は 202 が正解です。レスポンスボディに進捗確認 URL を返す、というパターンが定番です。

4xx:クライアントエラーの紛らわしい3つ

401 Unauthorized vs 403 Forbidden

名前と挙動が逆に感じる代表格です。

  • 401 Unauthorized:認証情報が無い/不正。「あなたが誰か分からない」
  • 403 Forbidden:認証は成功したが権限が無い。「あなたが誰かは分かったが、これは見せられない」

判定の流れ:

  1. リクエストに有効な認証情報があるか? → 無ければ 401
  2. 認証は通った。このリソースへのアクセス権はあるか? → 無ければ 403

API ゲートウェイのログを見るとき、401 がスパイクしていればトークン失効、403 がスパイクしていれば権限設定ミス、という切り分けができます。

404 Not Found vs 410 Gone

  • 404 Not Found:見つからない(理由は問わない)
  • 410 Gone:以前は存在したが恒久的に削除された

通常は 404 で十分ですが、Google などのクローラーは 410 を見つけるとインデックスから即座に削除します。一方 404 は「一時的に消えてるだけかも」と判断されてしばらくクロールされ続けます。

サイトのコンテンツを意図的に削除する場合は 410 を返したほうが SEO 的に綺麗です。

400 Bad Request vs 422 Unprocessable Entity

  • 400 Bad Request:リクエスト自体が壊れている(JSON パースエラー、必須ヘッダ欠落など)
  • 422 Unprocessable Entity:リクエスト形式は正しいが、内容のバリデーションに失敗

バリデーションエラーを 400 で返す API も多く、実装的にはどちらでも問題なく動きます。ただし「JSON 構文エラー」と「メールアドレス形式不正」を同じ 400 で返すと、クライアント側でリトライ判定がしづらくなります。422 を使い分けると:

  • 400 → リトライしても無駄
  • 422 → 入力を直してリトライすべき

という意味付けができ、エラーハンドリングが綺麗になります。

429 Too Many Requests

レート制限を返すコード。Retry-After ヘッダで「何秒後にリトライ可能か」を示すのが作法です。

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{"error": "rate limited", "retry_after_seconds": 60}

クライアント側はレスポンスヘッダの Retry-After を読んで指数バックオフを実装すると、サーバー側に優しい振る舞いになります。

5xx:サーバーエラーの使い分け

コード用途
500 Internal Server Error想定外のサーバーエラー全般
502 Bad Gateway上流(バックエンド/プロキシ先)からの応答が異常
503 Service Unavailableサーバーが一時的に利用不可(メンテ/過負荷)
504 Gateway Timeout上流のタイムアウト

500 を使うべきタイミング

500 を投げないことが正義」と思うとつい例外を全部キャッチして 200 + {success: false} で返したくなりますが、バグや想定外エラーは 500 を素直に返すほうが運用上正しいです。

  • ログに 500 が出ることでアラートが鳴る
  • 200 で success: false を隠すとバグが可視化されない

「ユーザー体験を壊さないように」と 200 で返す場合でも、サーバー側のログには 500 相当のエラー記録を残すべきです。

503 と Retry-After

メンテナンス中など「いつ復旧するか分かる」場合は 503 + Retry-After を返すと、賢いクライアントは適切に待機できます。

HTTP/1.1 503 Service Unavailable
Retry-After: Sat, 26 Apr 2026 12:00:00 GMT

ステータスコードと冪等性

REST API の設計では、HTTP メソッドの冪等性とステータスコードを合わせて考えます:

  • GET / PUT / DELETE は冪等:同じリクエストを何度送っても結果が同じ
  • POST は非冪等:送るたびに新しいリソースが作られる

冪等な操作は 200(成功)または 204(削除成功)で、同じリクエストの2回目以降も同じ結果を返すのが理想です。

非冪等な POST で「すでに作られている」場合、409 Conflict を返してクライアントに重複を伝えるパターンが使われます。

まとめ:迷ったときの判定フロー

処理は成功したか?
├── Yes
│   ├── 新規リソース作成? → 201
│   ├── 非同期処理開始?   → 202
│   ├── レスポンスボディ無し? → 204
│   └── それ以外           → 200
└── No
    ├── サーバー側の問題?
    │   ├── 一時的(メンテ・過負荷)? → 503
    │   ├── 上流の問題?               → 502 / 504
    │   └── 想定外バグ                 → 500
    └── クライアント側の問題?
        ├── 認証情報なし/不正 → 401
        ├── 権限なし           → 403
        ├── リソース存在しない → 404 or 410
        ├── レート制限         → 429
        ├── リクエスト構文NG   → 400
        └── バリデーションNG   → 422

API 実装時にこのフローを横に置いておくと、レビューでも判断が揃います。

特定のステータスコードの正確な意味や、レアな番号(418 I’m a teapot, 451 Unavailable For Legal Reasons など)を確認したいときは、本サイトの HTTP ステータスコード一覧で番号から逆引きできます。