HTTP ステータスコードはサーバーがクライアントに「リクエストがどうなったか」を伝える言語です。不可解な 422 に頭を抱えたり、401 と 403 のどちらを返すべきか迷ったことがある方のためのガイドです。各ステータスクラス・実務でよく遭遇するコード・クライアントとサーバーコードでの扱い方を解説します。
ステータスコードの構造
ステータスコードは3桁の整数で、5つのクラスに分類されます:
| クラス | 範囲 | 意味 |
|---|---|---|
| 1xx | 100–199 | 情報 — リクエスト受信、処理継続中 |
| 2xx | 200–299 | 成功 — リクエストの受信・理解・承認 |
| 3xx | 300–399 | リダイレクト — リクエスト完了のためにさらなるアクションが必要 |
| 4xx | 400–499 | クライアントエラー — リクエストに問題がある |
| 5xx | 500–599 | サーバーエラー — 有効なリクエストに対してサーバーが失敗 |
最初の桁がクラスを決定し、残りの2桁が具体的な状態を示します。
1xx:情報
アプリケーションレベルのコードではほとんど見かけません。サーバーが最終レスポンスの前に送ります。
100 Continue
サーバーがリクエストヘッダーを受信しました。クライアントはリクエストボディを送信してください。大きなアップロードに Expect: 100-continue と組み合わせて使用します。
101 Switching Protocols
サーバーは要求されたとおりに別のプロトコルに切り替えています(HTTP/1.1 から WebSocket へのアップグレードなど)。
2xx:成功
200 OK
リクエスト成功。レスポンスボディに要求されたリソースが含まれます。GET・POST・PUT・PATCH のデフォルト成功コードです。
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 を開くより速いです。