awesome-hacks
Docs

HTTPステータスコードの使い分け

200/400/401/403/404 など代表的なHTTPステータスコードの意味と、どの場面で返すべきかの判断基準

最終更新:2026/06/02

TL;DR

  • 200:正常に処理が完了した
  • 400:リクエストの内容が壊れているか不正(クライアント側のミス)
  • 401:認証されていない(ログインが必要)
  • 403:認証済みだが、そのリソースへのアクセス権がない
  • 404:指定したリソースが存在しない

401と403、400と422は特に混同しやすいので違いを明確に把握しておきたい。


目的

HTTPステータスコードは、サーバーがリクエストを受け取った後に「どういう結果になったか」をクライアントに伝えるための数値コードである。

適切なステータスコードを返すことで、

  • クライアント側が次のアクションを判断できる
  • エラーの原因がどこにあるか(クライアント / サーバー)を素早く特定できる
  • APIの品質・可読性が上がる

誤ったステータスコード(たとえばエラーなのに200を返すなど)を使うと、クライアント側のエラーハンドリングが壊れる原因になるため、正確に使い分けることが重要である。


ステータスコードの分類

HTTPステータスコードは100〜599の3桁の数値で、先頭の数字によって大まかなカテゴリが決まる。

範囲分類概要
1xx情報処理の途中(Webアプリでほぼ意識しない)
2xx成功リクエストが正常に処理された
3xxリダイレクト別のURLへの転送が必要
4xxクライアントエラーリクエスト側に問題がある
5xxサーバーエラーサーバー側に問題がある

200 OK

意味

リクエストが正常に処理された。最も基本的な成功レスポンス。

いつ返すか

  • GET:データ取得が成功したとき
  • PUT / PATCH:更新が成功したとき
  • DELETE:削除が成功し、レスポンスボディに結果を含むとき
GET /users/1
→ 200 OK

{
  "id": 1,
  "name": "田中 太郎",
  "email": "tanaka@example.com"
}

注意点

POST でリソースを新規作成した場合は 200 ではなく 201 Created を返すのが正確である。200 は「既存の処理が成功した」場面に使う。

また、エラーが発生しても200を返しレスポンスボディの中にエラーコードを入れる実装を見かけることがある。これは避けるべき設計で、クライアントのエラーハンドリングを複雑にする。


400 Bad Request

意味

リクエストの形式や内容がサーバーの期待と合わない。クライアント側のミスが原因。

いつ返すか

  • 必須パラメータが欠けている
  • パラメータの型が間違っている(数値のはずが文字列など)
  • JSONの構造が不正(パースできない)
  • バリデーションエラー(メールアドレスの形式が違う、パスワードが短すぎるなど)
POST /users
Content-Type: application/json

{
  "name": "",
  "email": "not-an-email"
}

→ 400 Bad Request

{
  "errors": [
    { "field": "name", "message": "名前は必須です" },
    { "field": "email", "message": "メールアドレスの形式が正しくありません" }
  ]
}

401・403・404との違い

400は「リクエストの中身自体がおかしい」場合に返す。認証や権限の話ではなく、入力内容の問題である。認証が必要な場合は401、権限がない場合は403を返す。

補足:422 Unprocessable Entity との使い分け

400と混同されやすいのが 422 Unprocessable Entity である。

コード使う場面
400リクエストの形式自体がおかしい(JSONが壊れているなど)
422形式は正しいが、内容のバリデーションが通らない

ただし、実際のAPIでは400をバリデーションエラーに使うケースも多く、プロジェクト内で統一されていれば大きな問題はない。


401 Unauthorized

意味

リクエストを処理するために認証が必要だが、認証情報が提供されていないか、無効である。名前は「Unauthorized(未認可)」だが、実際の意味は **「未認証(Unauthenticated)」**である点に注意。

いつ返すか

  • リクエストにトークン(Authorization ヘッダーなど)が含まれていない
  • トークンが期限切れ(JWT の有効期限切れなど)
  • トークンが改ざんされており検証に失敗した
  • ログインAPIでIDまたはパスワードが一致しない
GET /me
→ (Authorization ヘッダーなし)

→ 401 Unauthorized

{
  "message": "認証が必要です"
}
GET /me
Authorization: Bearer expired_token

→ 401 Unauthorized

{
  "message": "トークンの有効期限が切れています"
}

403との違い

コード状況ユーザーの状態
401誰なのかわからない未ログイン / トークン無効
403誰かはわかるが許可されていないログイン済みだがアクセス権がない

「ログインしていないからアクセスできない」→ 401
「ログインしているが、このリソースには触れない」→ 403


403 Forbidden

意味

リクエストは認識できたが、サーバーがそのリクエストを拒否している。認証済みであっても、そのリソースへのアクセス権が与えられていない。

いつ返すか

  • 一般ユーザーが管理者専用エンドポイントにアクセスした
  • 他のユーザーのデータを取得・更新しようとした
  • IPアドレス制限などで弾かれた
  • 特定のロール(権限)を持っていないユーザーが制限されたAPIを呼んだ
GET /admin/users
Authorization: Bearer valid_user_token

→ 403 Forbidden

{
  "message": "このリソースへのアクセス権がありません"
}
DELETE /posts/100
Authorization: Bearer valid_user_token
→ (投稿100は別のユーザーの記事)

→ 403 Forbidden

{
  "message": "他のユーザーのリソースを削除する権限がありません"
}

セキュリティ上の注意

「そのリソースが存在すること自体を隠したい」場合は、403ではなく404を返す設計にすることもある。たとえば、あるリソースの存在を知られたくない場合に403を返すと「存在はしているがアクセスできない」ことが露見してしまう。


404 Not Found

意味

リクエストされたリソースが存在しない。

いつ返すか

  • 指定したIDのユーザーや記事がDBに存在しない
  • 存在しないURLパスへのリクエスト
  • 削除済みのリソースへのアクセス
GET /users/9999
→ (ID 9999 のユーザーが存在しない)

→ 404 Not Found

{
  "message": "ユーザーが見つかりません"
}

403との使い分け(セキュリティ観点)

前述の通り、リソースの存在を隠したい場合に404を返すことがある。

ケース返すべきコード
存在しないリソース404
存在するが権限がない(存在を公開していい)403
存在するが権限がない(存在を隠したい)404

どちらを選ぶかはセキュリティ要件とAPI仕様の設計方針による。


よく使われるその他のコード

コード名前概要
201Createdリソースの新規作成が成功した(POSTの成功時)
204No Content成功したがレスポンスボディがない(DELETEの成功時など)
422Unprocessable Entity形式は正しいがバリデーションエラー
429Too Many Requestsレートリミットを超えた
500Internal Server Errorサーバー側で予期しないエラーが発生した
503Service Unavailableサーバーが一時的に利用不可(メンテナンスなど)

まとめ:判断フローチャート

リクエストを受け取った
  ↓
リクエストの形式・内容は正しいか?
  No → 400 Bad Request
  ↓ Yes
認証情報はあるか?(トークンは有効か?)
  No → 401 Unauthorized
  ↓ Yes
そのリソースへのアクセス権はあるか?
  No → 403 Forbidden
  ↓ Yes
リソースは存在するか?
  No → 404 Not Found
  ↓ Yes
処理を実行
  → 200 OK / 201 Created / 204 No Content

関連