HTTP — メソッド・ステータス・ヘッダー
HTTP はテキストベースの単純なプロトコルです。「何をしたいか」を 1 行目に書き、付随情報をヘッダーに並べ、本体を続ける。この構造さえ掴めば、API の設計もデバッグも見通しがよくなります。
この章で学ぶこと
- リクエストとレスポンスの構造と、メソッドがどこに現れるか
- 9 つの標準メソッドと、安全性・冪等性という 2 つの性質
- ステータスコードの 5 分類と、選び方
- HTTP/1.1 から HTTP/3 までで何が変わったか
TCP/IP の階層モデルを前提にします。HTTP はアプリケーション層のプロトコルです。
この章で扱わないこと
HTTP に隣接する話題は、それぞれの章とガイドが扱います。
| 観点 | 参照先 |
|---|---|
| 通信路の暗号化と証明書の検証 | TLS と証明書 |
| クッキーの属性とスコープ | クッキー |
| ブラウザのオリジン境界と CORS | オリジンと CORS |
| 再送の設計 (回数・間隔・冪等キー) | リトライと冪等性 |
| メソッドとステータスを API 設計で選ぶ手順 | Laravel API 開発ガイド — ルーティングと最初のエンドポイント |
| レスポンスの出力形式の設計 | Laravel API 開発ガイド — API リソース |
メッセージの構造
HTTP のやり取りは、リクエストとレスポンスの 2 種類のメッセージでできています。
リクエスト
POST /api/orders HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
{"itemId": 42, "quantity": 2}
| 部分 | 内容 |
|---|---|
| 1 行目 (リクエストライン) | メソッド + パス + プロトコルバージョン |
| 2 行目以降 (ヘッダー) | 付随情報。空行まで続く |
| 空行の後 (ボディ) | 送るデータ。メソッドによっては無い |
**メソッドはリクエストラインにだけ現れます。**ヘッダーで指定するものでもボディに入れるものでもありません。
レスポンス
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/orders/1001
{"id": 1001, "status": "accepted"}
| 部分 | 内容 |
|---|---|
| 1 行目 (ステータスライン) | プロトコルバージョン + ステータスコード + 理由句 |
| 2 行目以降 | ヘッダー |
| 空行の後 | ボディ |
**レスポンスにメソッドは含まれません。**レスポンスが持つのはステータスコードです。「何をしたか」はリクエスト側の情報で、返す側は「どうなったか」を返します。
標準メソッドは 9 つ
| メソッド | 何をするか | 安全 | 冪等 |
|---|---|---|---|
GET | リソースを取得する | ○ | ○ |
HEAD | GET と同じだがボディを返さない | ○ | ○ |
POST | データを送信する。状態変更や副作用を伴う | × | × |
PUT | リソースを送信内容で置き換える | × | ○ |
DELETE | リソースを削除する | × | ○ |
PATCH | リソースを部分的に変更する | × | × |
OPTIONS | 通信オプションを問い合わせる | ○ | ○ |
CONNECT | トンネルを確立する | × | × |
TRACE | 経路をループバック試験する | ○ | ○ |
**UPDATE と REMOVE は HTTP の標準メソッドではありません。**更新は PUT か PATCH、削除は DELETE を使います。SQL の UPDATE / DELETE や CRUD の語彙から連想して混同されやすいので、対応を押さえておきます。
| やりたいこと | SQL | HTTP |
|---|---|---|
| 作成 | INSERT | POST |
| 取得 | SELECT | GET |
| 全体を更新 | UPDATE | PUT |
| 一部を更新 | UPDATE | PATCH |
| 削除 | DELETE | DELETE |
安全性と冪等性
この 2 つは似ていますが別の性質です。
安全 (safe) — サーバーの状態を変更しない。読み取り専用。
冪等 (idempotent) — 同じリクエストを何回送っても、結果の状態が 1 回送ったときと同じになる。
安全 ⊂ 冪等
安全なメソッドは必ず冪等です。何も変えないので、何回やっても状態は同じだからです。逆は成り立ちません。
DELETE が分かりやすい例です。**状態を変えるので安全ではありませんが、冪等です。**同じリソースを 2 回消しても、最終的な状態は「そのリソースが無い」で変わりません。2 回目が 404 を返したとしても、状態が冪等であることとレスポンスが同じであることは別の話です。
POST が冪等でないのは、送るたびに新しいリソースが増えるからです。注文 API を 2 回叩けば注文が 2 件できます。これが「送信ボタンの二度押し」が問題になる理由で、対策として冪等キー (同じキーのリクエストは 1 回しか処理しない) を導入することがあります。
PUT と PATCH の差もここに出ます。PUT は「この内容で置き換える」なので何回やっても同じ状態になり冪等です。PATCH は「この差分を適用する」なので、差分の内容によっては冪等になりません (たとえば「在庫を 1 減らす」という差分)。
**この性質が効くのは再送の判断です。**通信が途中で切れて応答が分からないとき、冪等なメソッドなら安全に再送できます。冪等でないなら、再送すると二重に処理される危険があります。再送を何回・どの間隔で行うかはリトライと冪等性で扱います。
ステータスコード
先頭の数字で 5 つに分かれます。
| 分類 | 意味 | 代表例 |
|---|---|---|
1xx | 情報 | 101 Switching Protocols |
2xx | 成功 | 200 OK / 201 Created / 204 No Content |
3xx | リダイレクト | 301 Moved Permanently / 304 Not Modified |
4xx | クライアント側の誤り | 400 / 401 / 403 / 404 / 409 / 422 / 429 |
5xx | サーバー側の誤り | 500 / 502 / 503 |
4xx と 5xx の境目が実務では重要です。リクエストの内容に問題があるなら 4xx、サーバーが原因なら 5xx です。ここを取り違えると、監視のアラートが的外れになります。バリデーションエラーを 500 で返していると、正常な入力ミスがサーバー障害として通知されます。
紛らわしい組み合わせを整理します。
| 使う場面 | |
|---|---|
401 Unauthorized | 認証が済んでいない。誰か分からない |
403 Forbidden | 認証は済んでいるが認可が下りない。誰かは分かるが権限が無い |
404 Not Found | 存在しない。存在を隠したい場合に 403 の代わりに使うこともある |
400 Bad Request | リクエストの形式が不正 |
422 Unprocessable Content | 形式は正しいが内容が業務ルールに反する |
409 Conflict | 現在の状態と競合する (重複登録、版数の食い違い) |
ヘッダー
ヘッダーは「本文以外の付随情報」を運びます。よく使うものを役割で分けます。
| 役割 | 例 |
|---|---|
| 中身の説明 | Content-Type / Content-Length / Content-Encoding |
| 認証 | Authorization / WWW-Authenticate |
| キャッシュ | Cache-Control / ETag / Last-Modified |
| 交渉 | Accept / Accept-Language / Accept-Encoding |
| 状態の保持 | Cookie / Set-Cookie |
| オリジン間の制御 | Origin / Access-Control-Allow-Origin / Access-Control-Allow-Headers |
| セキュリティ | Content-Security-Policy / Strict-Transport-Security |
Content-Type は本文の形式に加えて、どの文字コードで読むかも運びます。送り手と受け手でここがずれると文字化けします (文字コード)。
HTTP はステートレスなプロトコルです。サーバーはリクエスト間で状態を覚えません。ログイン状態を保つには、クッキーやトークンを毎回のリクエストに載せて、サーバー側で照合します。「状態を持たない」のが原則で、状態が要るなら明示的に運ぶ、という設計です。
バージョンによる違い
| 登場 | トランスポート | 主な変更 | |
|---|---|---|---|
| HTTP/1.1 | 1997 | TCP | 接続の再利用 (keep-alive)。1 接続で 1 リクエストずつ処理する |
| HTTP/2 | 2015 | TCP | 多重化。1 接続で複数のやり取りを並行させる。ヘッダー圧縮 |
| HTTP/3 | 2022 | UDP (QUIC) | TCP をやめて接続確立を短縮。パケット欠落の影響を局所化する |
接続確立が短いのは、QUIC が TLS 1.3 のハンドシェイクを自分の接続確立に組み込んでいるためです。TCP の確立とセキュアな接続の確立が別々の往復にならず、1 往復で済みます。
HTTP/1.1 では、1 つの接続で前のレスポンスが返るまで次を送れないという制約がありました (ヘッドオブラインブロッキング)。これを避けるためにブラウザは同一ホストへ複数の接続を張っていました。
HTTP/2 は 1 つの接続に複数のストリームを流せるようにしてこれを解消しましたが、TCP の層では依然として 1 つのパケットが落ちると後続がすべて待たされます。HTTP/3 が UDP へ移ったのは、この TCP 由来の詰まりを避けるためです。
**メソッドやステータスコードの意味はバージョンをまたいで変わりません。**変わったのは運び方だけです。
よくある誤解
「GET でも、ついでに状態を変えてかまわない」 — GET は安全なメソッドとして定義されています。「一覧を取得したついでに既読フラグを立てる」ような設計にすると、クローラやプリフェッチが勝手に状態を変えてしまいます。読み取りに副作用を持たせるなら POST を使います。
「POST は新規作成専用」 — 作成に使われることが多いだけで、そう定められてはいません。冪等でなくてよく、リソース指向に収まらない操作を表せる汎用のメソッドです。「在庫を 1 つ減らす」を POST にするのはこの性質によります。
「DELETE は冪等ではない」 — 冪等です。2 回目が 404 を返しても、結果の状態は変わりません。冪等性は状態についての性質で、レスポンスが同じかどうかではありません。
「安全なら冪等、冪等なら安全」 — 安全なら必ず冪等ですが、逆は成り立ちません。PUT と DELETE は冪等ですが安全ではありません。
確認問題
問 1. 社内の API 設計レビューで「更新は UPDATE、削除は REMOVE メソッドを使う」という案が出ました。どう指摘しますか。
答え: どちらも HTTP の標準メソッドではありません。更新は PUT か PATCH、削除は DELETE を使います。
標準メソッドは次の 9 つです。
GET / HEAD / POST / PUT / DELETE / PATCH / OPTIONS / CONNECT / TRACE
UPDATE と REMOVE が混同されやすいのは、SQL や CRUD の語彙に引きずられるためです。対応を整理しておきます。
| やりたいこと | SQL | HTTP |
|---|---|---|
| 全体を置き換える | UPDATE | PUT |
| 一部だけ変える | UPDATE | PATCH |
| 削除する | DELETE | DELETE |
独自のメソッド名を使うと、プロキシやキャッシュや API クライアントが解釈できません。HTTP のメソッドは共通語彙なので、勝手に増やすと途中の中継機が扱えなくなります。
「メソッドで表現できない操作をしたい」という要求なら、POST を使ってパスや本文で操作を表すのが現実的な着地点です。
問 2. 「在庫を 1 つ減らす」API を作ります。POST・PUT・PATCH のどれが適切ですか。
答え: POST
冪等性で判断します。「1 つ減らす」は送るたびに結果が変わるので冪等ではありません。冪等でない操作に冪等なメソッド (PUT / DELETE) を割り当てると、クライアントやプロキシが「再送しても安全」と判断して二重に減らす危険があります。
PATCH も冪等ではないので候補になりますが、PATCH は「リソースの部分更新」を表すメソッドです。「減らす」という手続きを表現するなら、リソース指向から外れた操作として POST を使うほうが誤解が少なくなります。
冪等にしたいなら、設計を変えて結果の値を送る形にします。
PUT /api/items/42/stock
{"quantity": 9}
これなら何回送っても在庫は 9 になり冪等です。ただし他のクライアントとの同時更新で上書きが起きるので、版数を使った衝突検出が別途要ります。
問 3. ログインしていないユーザーが管理画面の API を叩きました。返すべきステータスコードは何ですか。ログイン済みだが権限が無い場合はどうですか。
答え: 未ログインなら 401 Unauthorized、権限不足なら 403 Forbidden
区別の軸は認証か認可かです。
| 状況 | コード | 意味 |
|---|---|---|
| トークンが無い、期限切れ、不正 | 401 | 誰か分からない。認証をやり直してほしい |
| 認証済みだが権限が足りない | 403 | 誰かは分かるが許可できない。やり直しても結果は変わらない |
401 は「認証すれば通るかもしれない」、403 は「認証し直しても通らない」という違いです。クライアント側の挙動も変わり、401 ならログイン画面へ誘導し、403 ならエラーを表示します。この区別の背景は認証と認可で扱います。
なお、リソースの存在自体を隠したい場合に 403 の代わりに 404 を返す設計もあります。「権限が無い」と返すことで、そのリソースが存在する事実を漏らしてしまうためです。
まとめ
- HTTP メッセージはリクエストライン (またはステータスライン) + ヘッダー + ボディの構造です
- **メソッドはリクエストラインにだけ現れます。**ヘッダーやボディには置かれず、レスポンスにも含まれません
- 標準メソッドは 9 つ。
UPDATEとREMOVEは含まれません - 安全は状態を変えないこと、冪等は何回送っても結果の状態が同じこと。安全なら冪等ですが逆は成り立ちません
DELETEは安全ではありませんが冪等です- ステータスは
4xxがクライアント側、5xxがサーバー側の誤りです。401は認証、403は認可の問題です - HTTP はステートレスです。状態はクッキーやトークンで明示的に運びます
- バージョンで変わったのは運び方だけで、メソッドやステータスの意味は同じです
- Laravel API 開発ガイド — ルーティングと最初のエンドポイント — メソッドとステータスコードを API 設計で選ぶ
- MDN — HTTP リクエストメソッド
- RFC 9110 — HTTP Semantics — メソッドの定義と、安全性・冪等性の規定
- RFC 5789 — PATCH Method for HTTP —
PATCHだけは別の RFC で定義されています