HTTP ステータスコードはサーバーがクライアントに「リクエストがどうなったか」を伝える言語です。不可解な 422 に頭を抱えたり、401403 のどちらを返すべきか迷ったことがある方のためのガイドです。各ステータスクラス・実務でよく遭遇するコード・クライアントとサーバーコードでの扱い方を解説します。

ステータスコードの構造

ステータスコードは3桁の整数で、5つのクラスに分類されます:

クラス範囲意味
1xx100–199情報 — リクエスト受信、処理継続中
2xx200–299成功 — リクエストの受信・理解・承認
3xx300–399リダイレクト — リクエスト完了のためにさらなるアクションが必要
4xx400–499クライアントエラー — リクエストに問題がある
5xx500–599サーバーエラー — 有効なリクエストに対してサーバーが失敗

最初の桁がクラスを決定し、残りの2桁が具体的な状態を示します。

1xx:情報

アプリケーションレベルのコードではほとんど見かけません。サーバーが最終レスポンスの前に送ります。

100 Continue

サーバーがリクエストヘッダーを受信しました。クライアントはリクエストボディを送信してください。大きなアップロードに Expect: 100-continue と組み合わせて使用します。

101 Switching Protocols

サーバーは要求されたとおりに別のプロトコルに切り替えています(HTTP/1.1 から WebSocket へのアップグレードなど)。

2xx:成功

200 OK

リクエスト成功。レスポンスボディに要求されたリソースが含まれます。GETPOSTPUTPATCH のデフォルト成功コードです。

201 Created

新しいリソースが作成されました。エンティティを作成する POST リクエストの成功に使います。レスポンスには新しいリソースを指す Location ヘッダーを含めるべきです。

HTTP/1.1 201 Created
Location: /api/users/456

204 No Content

リクエストは成功しましたが、レスポンスボディはありません。DELETE 操作や、更新されたリソースを返さない PUT/PATCH に使います。

HTTP/1.1 204 No Content

206 Partial Content

サーバーがリソースの一部(範囲リクエスト)を配信しています。ビデオストリーミングや再開可能なダウンロードで使用されます。Content-Range ヘッダーが必要です。

3xx:リダイレクト

301 Moved Permanently

リソースが新しい URL に恒久的に移動しました。クライアントと検索エンジンはブックマークを更新すべきです。SEO の評価が新しい URL に引き継がれます。安定した API でルートの名前を変更する際に使います。

302 Found(一時リダイレクト)

リソースは一時的に別の URL にあります。クライアントはブックマークを更新しないでください。恒久的な意図なのに 302 を使わないでください。Google は 302 経由では完全な PageRank を引き渡しません。

304 Not Modified

キャッシュされたバージョンはまだ有効です。ETag / Last-Modified の条件付きリクエストで使用されます。サーバーはボディを送信せず、クライアントはキャッシュを使用します。API と CDN のパフォーマンスに重要です。

307 Temporary Redirect / 308 Permanent Redirect

302/301 に似ていますが、HTTP メソッドが保持されます。307 リダイレクトされた URL への POST は新しい URL への POST のままです。フォーム送信など、メソッドの保持が重要な場合は 302/301 の代わりにこれらを使います。

4xx:クライアントエラー

400 Bad Request

リクエストが不正な形式です。サーバーは解析できません。構文エラー・無効な JSON・必須フィールドの欠落・型の不一致に使います。

HTTP/1.1 400 Bad Request
{ "error": "Invalid JSON body" }

401 Unauthorized

認証が必要で提供されていないか、認証情報が無効です。レスポンスには WWW-Authenticate ヘッダーを含めるべきです。名前は紛らわしいですが、実質的には「未認証」という意味です。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

403 Forbidden

サーバーはリクエストを理解しましたが、認可を拒否します。クライアントは認証済みですが権限がありません。ユーザーがログインしているが必要なロールを持っていない場合に 403 を返します。

401 vs 403:401 = 「あなたは誰ですか?」、403 = 「あなたのことはわかりますが、これはできません」

404 Not Found

リソースが存在しません。リソースの存在を明かすことがセキュリティ問題になる場合、未認可のユーザーからリソースを隠すためにも使います(403 の代わりに)。

405 Method Not Allowed

HTTP メソッドはこのエンドポイントでサポートされていません。有効なメソッドを列挙した Allow ヘッダーを必ず含めてください。

HTTP/1.1 405 Method Not Allowed
Allow: GET, POST

409 Conflict

リクエストがリソースの現在の状態と競合します。典型的な使用例:既存のメールでユーザーを作成しようとする、同じレコードへの同時編集など。

410 Gone

リソースは存在していましたが、恒久的に削除されました。404 とは異なり、410 はクライアントと検索エンジンにこの URL を再リクエストしないよう伝えます。

422 Unprocessable Entity

リクエストは正しい形式でしたが、セマンティックバリデーションに失敗しました。JSON が正しく解析できるが、ビジネスルールに違反している場合に使います(開始日より後の終了日、マイナスの数量など)。

429 Too Many Requests

レートリミットを超えました。クライアントにいつ再試行できるかを伝える Retry-After ヘッダーを含めてください。

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0

5xx:サーバーエラー

500 Internal Server Error

サーバーが予期しない状態に遭遇しました。未処理の例外のキャッチオール。詳細はサーバー側でログに記録し、スタックトレースはクライアントに公開しないでください。

502 Bad Gateway

ゲートウェイまたはプロキシがアップストリームサーバーから無効なレスポンスを受信しました。ロードバランサーがアプリケーションサーバーに到達できないときによく発生します。

503 Service Unavailable

サーバーは一時的にリクエストを処理できません(過負荷・メンテナンス・サーキットブレーカーのオープン)。計画されたダウンタイムの場合は Retry-After ヘッダーを使います。

504 Gateway Timeout

ゲートウェイまたはプロキシがアップストリームから適時のレスポンスを受信できませんでした。よくある原因:遅いデータベースクエリ、下流 API のタイムアウト。

適切なステータスコードの選び方

一般的な API シナリオの決定木:

シナリオコード
GET がリソースを返す200
POST がリソースを作成201
DELETE が成功(ボディなし)204
無効なリクエストボディ / 構文エラー400
認証トークンが欠落または無効401
有効なトークンだが権限不足403
リソースが見つからない404
メールが既に登録済み(重複)409
バリデーション失敗(セマンティクス)422
未処理の例外500

コードでのステータスコード処理

JavaScript(fetch)

async function apiRequest(url, options) {
  const res = await fetch(url, options);

  if (res.ok) {
    return res.json();          // 200–299
  }

  if (res.status === 401) {
    redirectToLogin();
    return;
  }

  if (res.status === 429) {
    const retryAfter = res.headers.get('Retry-After');
    throw new Error(`Rate limited. Retry after ${retryAfter}s`);
  }

  const error = await res.json().catch(() => ({}));
  throw new Error(error.message ?? `HTTP ${res.status}`);
}

Python(httpx / requests)

import httpx

with httpx.Client() as client:
    r = client.get("https://api.example.com/resource")

    if r.status_code == 200:
        data = r.json()
    elif r.status_code == 404:
        raise ValueError("Resource not found")
    elif r.status_code == 429:
        retry_after = r.headers.get("Retry-After", "60")
        raise Exception(f"Rate limited, retry in {retry_after}s")
    else:
        r.raise_for_status()

クイックリファレンス

ZeroTool で HTTP ステータスコードを即座に調べる →

コード番号またはキーワードで検索し、クラス別(1xx〜5xx)に閲覧して、ブラウザを離れずに説明と典型的なユースケースを確認できます。API レスポンスのデバッグ中に別のタブで MDN を開くより速いです。