この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:5
ページ更新者:atom
更新日時:2026-06-11 07:10:02

タイトル: httpステータスコードの付与
SEOタイトル: PHP / Laravel HTTP ステータスコード付与完全ガイド

この記事の要点
  • 素の PHP: http_response_code(404) が最も簡単。header("HTTP/1.1 404 Not Found") でも可
  • Laravel: response()->view($view, 404) / response()->json([...], 422) / abort(404) / abort_if($cond, 403)
  • Symfony Response 定数: Response::HTTP_NOT_FOUND 等を使うとマジックナンバーを避けられる
  • API でのバリデーションエラーは 422 Unprocessable Entity、フォーマットそのものが壊れているなら 400 Bad Request
  • 正常系の 204 No Content(DELETE 成功)と 201 Created(POST 成功)も忘れずに付ける

HTTP ステータスコードを付与する 3 つの場面

PHP / Laravel で HTTP ステータスコードを返す場面は大きく分けて 3 つあります:

  1. 素の PHP でレスポンスを返す(小さなスクリプト、独自フレームワーク)
  2. Laravel で Response オブジェクトを返す(HTML / JSON)
  3. 例外を投げる 形で中断する(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 ステータスコード一覧

コード意味使う場面
200OKGET 正常終了 / 一般的成功
201CreatedPOST でリソース新規作成成功
204No ContentDELETE 成功、PUT 後にボディ返さない
301Moved Permanently恒久リダイレクト(SEO 用、www 統一)
302Found一時リダイレクト(旧 Moved Temporarily)
303See OtherPOST 後の GET 誘導(PRG パターン)
304Not ModifiedETag / Last-Modified キャッシュ有効
400Bad Requestリクエスト構文エラー、JSON パース失敗
401Unauthorized未認証(認証必要)
403Forbidden認証済だが権限不足
404Not Foundリソース存在せず
419Page Expired (Laravel 独自)CSRF トークン切れ
422Unprocessable Entityバリデーションエラー(フォーム/JSON)
429Too Many Requestsレートリミット超過
500Internal Server Errorサーバ側の予期せぬ例外
502Bad Gatewayリバプロ→アプリ通信失敗
503Service Unavailableメンテ中 / 過負荷

API での 422 vs 400 の使い分け

状況コード
JSON が壊れている / 必須フィールド欠落(パース不能)400{"name": のような壊れた JSON
JSON は正しいが値が業務ルール違反(メール形式 NG 等)422メール未入力、パスワード短すぎ
Content-Type 未対応415XML 送信したが JSON のみ受付
認証ヘッダ無し401Bearer トークン未付与
認証済だが該当リソース閲覧不可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 を返し、ボディに詳細を入れるのが正解。フロント / 監視ツールが正しく扱えます。