タイトル: httpステータスコードの付与
SEOタイトル: PHP / Laravel HTTP ステータスコード付与完全ガイド
| この記事の要点 |
|
HTTP ステータスコードを付与する 3 つの場面
PHP / Laravel で HTTP ステータスコードを返す場面は大きく分けて 3 つあります:
- 素の PHP でレスポンスを返す(小さなスクリプト、独自フレームワーク)
- Laravel で Response オブジェクトを返す(HTML / JSON)
- 例外を投げる 形で中断する(abort / throw new HttpException)
素の PHP でステータスコードを返す
<?php
// ✅ 推奨: PHP 5.4+ 標準
http_response_code(404);
echo 'Not Found';
exit;
// 取得もできる
$current = http_response_code(); // 200 (デフォルト)
// 旧来の方法(HTTP プロトコルバージョンの揺れに注意)
header('HTTP/1.1 404 Not Found');
header('HTTP/2 404 Not Found'); // HTTP/2 で動かす場合
// CGI 環境では Status: ヘッダを使うことも
header('Status: 404 Not Found');
http_response_code() は HTTP プロトコルバージョン (HTTP/1.0 / 1.1 / 2 / 3) を自動で判別してくれるため、こちらを使うのが安全です。
Laravel でステータスコードを付ける
// 1. ビューを 404 で返す
return response()->view('errors.404', [], 404);
// 2. JSON API でバリデーションエラー (422)
return response()->json([
'message' => 'The given data was invalid.',
'errors' => ['email' => ['The email field is required.']],
], 422);
// 3. シンプルなテキストレスポンス
return response('Created', 201);
// 4. リダイレクト + ステータスコード
return redirect('/login', 302);
return redirect()->route('home')->setStatusCode(303);
// 5. ヘッダ付きで返す
return response()->json($data, 201)
->header('Location', '/users/' . $user->id);
// 6. ファクトリ的に作る
return response()->noContent(); // 204
return response()->json($data, Response::HTTP_CREATED);
abort() / abort_if() / abort_unless() を活用
Laravel で「特定条件なら例外停止」のパターンには abort() 系が便利です:
use Illuminate\Http\Response;
// 即座に 404 を返して終了
abort(404);
abort(404, 'Article not found'); // メッセージ付き
abort(Response::HTTP_NOT_FOUND); // 定数
// 条件付き
abort_if($user->isBanned(), 403, 'Banned');
abort_unless($user->canEdit($post), 403);
// カスタムレスポンス
abort(response()->json(['error' => 'gone'], 410));
// findOrFail / firstOrFail も内部で 404 abort
$post = Post::findOrFail($id); // 無ければ ModelNotFoundException → 404
Symfony Response 定数を使う
マジックナンバー (200, 404) を直接書かず、定数を使うと意図が明確になります:
use Symfony\Component\HttpFoundation\Response;
// または Laravel では Illuminate\Http\Response も同じ定数を持つ
return response()->json($data, Response::HTTP_OK); // 200
return response()->json($data, Response::HTTP_CREATED); // 201
return response()->noContent(); // 204
return response()->json($data, Response::HTTP_BAD_REQUEST); // 400
return response()->json($data, Response::HTTP_UNAUTHORIZED); // 401
return response()->json($data, Response::HTTP_FORBIDDEN); // 403
return response()->json($data, Response::HTTP_NOT_FOUND); // 404
return response()->json($data, Response::HTTP_UNPROCESSABLE_ENTITY); // 422
return response()->json($data, Response::HTTP_TOO_MANY_REQUESTS); // 429
return response()->json($data, Response::HTTP_INTERNAL_SERVER_ERROR); // 500
よく使う HTTP ステータスコード一覧
| コード | 意味 | 使う場面 |
|---|---|---|
| 200 | OK | GET 正常終了 / 一般的成功 |
| 201 | Created | POST でリソース新規作成成功 |
| 204 | No Content | DELETE 成功、PUT 後にボディ返さない |
| 301 | Moved Permanently | 恒久リダイレクト(SEO 用、www 統一) |
| 302 | Found | 一時リダイレクト(旧 Moved Temporarily) |
| 303 | See Other | POST 後の GET 誘導(PRG パターン) |
| 304 | Not Modified | ETag / Last-Modified キャッシュ有効 |
| 400 | Bad Request | リクエスト構文エラー、JSON パース失敗 |
| 401 | Unauthorized | 未認証(認証必要) |
| 403 | Forbidden | 認証済だが権限不足 |
| 404 | Not Found | リソース存在せず |
| 419 | Page Expired (Laravel 独自) | CSRF トークン切れ |
| 422 | Unprocessable Entity | バリデーションエラー(フォーム/JSON) |
| 429 | Too Many Requests | レートリミット超過 |
| 500 | Internal Server Error | サーバ側の予期せぬ例外 |
| 502 | Bad Gateway | リバプロ→アプリ通信失敗 |
| 503 | Service Unavailable | メンテ中 / 過負荷 |
API での 422 vs 400 の使い分け
| 状況 | コード | 例 |
|---|---|---|
| JSON が壊れている / 必須フィールド欠落(パース不能) | 400 | {"name": のような壊れた JSON |
| JSON は正しいが値が業務ルール違反(メール形式 NG 等) | 422 | メール未入力、パスワード短すぎ |
| Content-Type 未対応 | 415 | XML 送信したが JSON のみ受付 |
| 認証ヘッダ無し | 401 | Bearer トークン未付与 |
| 認証済だが該当リソース閲覧不可 | 403 | 他人の記事を編集しようとした |
Laravel バリデーションは自動で 422
// 標準の $request->validate() は失敗時 422 を返す
public function store(Request $request)
{
$validated = $request->validate([
'email' => 'required|email',
'name' => 'required|string|max:255',
]);
// ↓ JSON リクエストなら 422 + errors
// ↓ HTML リクエストなら 302 + フラッシュメッセージ
}
// FormRequest クラスを使う場合
class StoreUserRequest extends FormRequest {
public function rules(): array {
return ['email' => 'required|email'];
}
}
public function store(StoreUserRequest $req) {
// 自動でバリデート→失敗時 422
}
ヘッダとセットで返すケース
// 201 Created + Location ヘッダ(REST 標準)
return response()->json($post, 201)
->header('Location', route('posts.show', $post));
// 429 Too Many Requests + Retry-After
return response()->json(['error' => 'rate limited'], 429)
->header('Retry-After', 60); // 60 秒後にリトライ
// 401 Unauthorized + WWW-Authenticate(仕様準拠)
return response('Unauthorized', 401)
->header('WWW-Authenticate', 'Bearer realm="api"');
// 503 Service Unavailable + Retry-After(メンテ中)
return response('Maintenance', 503)
->header('Retry-After', 3600);
FAQ
Q: http_response_code() と header() の違いは?
A: http_response_code() はバージョン非依存で安全。header() は HTTP/1.1 等を直書きするので HTTP/2/3 環境で問題になることがあります。
Q: 404 を返したいだけなのにレイアウトを変える必要がある?
A: Laravel なら resources/views/errors/404.blade.php を用意すれば abort(404) 時に自動で使われます。419 / 500 など個別ビューも作れます。
Q: 200 で {"error": "..."} を返すパターンは?
A: アンチパターンです。HTTP の仕様に則って 4xx / 5xx を返し、ボディに詳細を入れるのが正解。フロント / 監視ツールが正しく扱えます。