HTTP
HTTP ・ えいちてぃーてぃーぴー
Webの通信プロトコル。クライアントが要求しサーバが応答する、1往復の繰り返しでできている。
✦ 30秒で試す — 航海返事が来ない。送り直していい要求と、ダメな要求がある同じ要求を2回送っても、害がないのはどれ?予想してみる概要
HTTP(HyperText Transfer Protocol)は、ブラウザとサーバが話すときの「会話のルール」です。クライアントが「この URL のものをください」とリクエストを送り、サーバがステータスコードと本文(ボディ)でレスポンスを返します。この1往復が HTTP の基本単位で、Web の通信はすべてこの往復の積み重ねでできています。
ページを1枚開くだけでも、HTML、CSS、JavaScript、画像と、裏では数十回の往復が起きています。ページの取得だけでなく、アプリがサーバの機能を呼び出す API(多くは JSON をやり取りします)にも同じ HTTP が使われます。フロントエンドからバックエンドまで、Web の開発者がいちばんよく触れるプロトコルです。
メソッド、ステータスコード、ヘッダ、キャッシュの制御など、Web 開発の語彙の多くは HTTP の仕様から来ています。HTTP を読めるようになることは、Web の共通語を身につけることとほぼ同じです。
なぜ生まれたか
1989年、CERN(欧州原子核研究機構)にいたティム・バーナーズ=リーは、研究所に散らばった文書を互いにリンクでたどれる仕組みを提案しました。これが World Wide Web です。文書の住所を表す URL、文書を書く HTML、そして文書を取り寄せるプロトコルの3点が必要で、この取り寄せ役として作られたのが HTTP です。最初の版(のちに HTTP/0.9 と呼ばれます)は「GET とパスを送ると HTML が返ってきて接続が切れる」だけの、1行で書けるほど小さなものでした。
すでに FTP などのファイル転送の仕組みはありましたが、ログインやディレクトリ移動の手順が重く、「リンクを押したらすぐ文書が届く」体験には向きませんでした。HTTP は「1回のリクエストに1回のレスポンスを返して終わり」という割り切りを選び、実装が簡単だったので一気に広まりました。その後 1996年の HTTP/1.0 でヘッダとステータスコードが入り、画像やフォームの送信も運べる汎用のプロトコルになりました。
この割り切りには代償もありました。サーバは前のやり取りを覚えない(ステートレス)ので、「ログインしている」という状態を保てません。この穴を埋めるために、あとから Cookie とセッションが生まれます。
詳細
メッセージの形 — 開始行・ヘッダ・空行・ボディ
HTTP/1.1 までのメッセージは、人が読めるテキストでできています。リクエストもレスポンスも同じ4つの部分から成ります。1行目の開始行(リクエストなら「メソッド・パス・バージョン」、レスポンスなら「バージョン・ステータスコード・理由」)、続くヘッダ(「名前: 値」の行の並び)、ヘッダの終わりを示す空行、そしてボディです。GET のようにボディのないリクエストもあります。
1つのページを開くときのやり取りを時間の順に並べると、次のようになります。まず HTML を取り、その中に書かれた CSS や画像を追加で取りにいきます。
メソッドとステータスコード
メソッドはリクエストの「動詞」です。取得の GET、送信の POST、置き換えの PUT、部分更新の PATCH、削除の DELETE などがあり、この動詞を「リソース(名詞)」に当てて使い分ける設計が REST API です。ステータスコードは結果の要約で、2xx が成功(200 OK)、3xx がリダイレクトやキャッシュの再利用(301・302・304)、4xx がクライアント側の誤り(400・401・403・404)、5xx がサーバ側の障害(500・502・503)です。「4xx ならリクエストを直す、5xx なら時間をおいて再試行する価値がある」と読めると、障害の切り分けが速くなります。
メソッドには、仕様で決められた2つの性質があります。1つは安全(safe)で、「サーバの状態を変えない」ことです。GET と HEAD は安全なので、ブラウザの先読みやクローラーが勝手に送っても問題ありません。もう1つは冪等性(idempotent)で、「同じリクエストを1回送っても何回送っても、最終的なサーバの状態が同じ」ことです。安全なメソッドは冪等でもありますが、逆は成り立ちません。PUT(この内容で置き換える)と DELETE(消す)は状態を変えるので安全ではありませんが、2回送っても結果は同じなので冪等です。POST はどちらでもなく、「注文する」を2回送れば注文が2件できます。
この区別が効くのは、応答が返ってこなかったときです。通信が途中で切れると、クライアントには「サーバに届かなかったのか、処理されたが応答だけ失われたのか」が分かりません。冪等なリクエストならもう一度送ればよいので、リトライと指数バックオフを自動でかけられます。POST をむやみに再送すると二重注文になるので、冪等キー(リクエストごとの一意な ID)を付けてサーバ側で重複を捨てる、といった工夫が必要になります。
ヘッダとステートレス
ヘッダはメッセージの付帯情報です。ボディの形式を示す Content-Type、キャッシュしてよいかと期限を決める Cache-Control、状態を運ぶ Set-Cookie と Cookie、認証情報を載せる Authorization、別のオリジンからの呼び出しを許すかを伝える CORS の Access-Control-Allow-Origin などがあります。Web アプリの振る舞いの多くは、コードよりもヘッダで決まっています。
HTTP はステートレスで、サーバはリクエストを1つずつ独立に扱います。この割り切りのおかげで、どのサーバがどのリクエストを受けてもよくなり、ロードバランサで台数を増やす水平分散と相性が良くなりました。ログイン状態のように続きのある状態は、Cookie に載せた ID と、サーバ側のセッションの組で表します。
バージョンの進化 — 意味はそのまま、運び方だけ変わる
HTTP は TCP/IP の上で動く前提で育ってきました。HTTP/1.0 はリクエストのたびに TCP の接続を張り直していましたが、HTTP/1.1(1997年、現在の仕様は RFC 9112)で接続の使い回し(keep-alive)が標準になりました。それでも1本の接続では「前の応答が返るまで次を送れない」ため、ブラウザは同じ相手に6本前後の接続を並べてしのいでいました。
2015年の HTTP/2 は、1本の接続の中に複数のやり取りを混ぜて流す「多重化」で、この待ち行列(Head-of-Line ブロッキング)を HTTP の層から取り除きました。2022年の HTTP/3とQUIC は、TCP の代わりに UDP の上に作った QUIC を使い、パケットが1つ落ちたときに全部が止まる TCP の層の待ち行列まで解消しています。どのバージョンでも、メソッド・ステータスコード・ヘッダという意味の部分は同じです(RFC 9110 に共通の意味としてまとめられています)。変わったのは「どう運ぶか」だけです。
実務での視点と落とし穴
HTTP を読めると、まずデバッグが速くなります。ブラウザの開発者ツールの Network タブでリクエストとレスポンスを見れば、「404 だからパスが違う」「CORS のプリフライト(本番のリクエストの前に OPTIONS で許可を尋ねる往復)で止まっている」「Cache-Control が長すぎて更新が反映されない」といった原因を自分で特定できます。
素の HTTP は平文で流れるので、経路の途中で読まれも書き換えられもします。現代の Web では TLS で暗号化した HTTPS が前提で、素の HTTP は開発環境の外ではほとんど使いません。また、HTTP は「クライアントが聞いたらサーバが答える」形なので、サーバから好きなときに知らせたい用途には、HTTP の接続から切り替えて使う WebSocket や、応答を流し続ける Server-Sent Events を使います。
試問
通信が途中で切れると、アプリには「要求がサーバに届いたのか、処理されたが返事だけ失われたのか」が分かりません。次の8つの要求を、返事が来ないときにそのまま送り直してよいかどうかの性質で分けてください。
返事が途切れた8つの要求を、「状態を変えない」「変えるが何回送っても同じ」「送るたびに結果が重なる」に仕分けよ
項目を選んでから、入れる箱を選びます
