ネットワーク基礎:TCP/IP・HTTP・DNS・CDN
HTTP の基本
この回でやること
HTTP リクエストとレスポンスの構造、ステータスコードを理解します。
- 読む 約 10 分
- 最後にクイズ 1 問
「画面が真っ白です」と言われて、どこから見るか
不具合の報告が来たとき、最初に知りたいのは「ブラウザが何を送って、サーバーが何を返したか」です。ここが読めないと、直すのが自分なのか相手なのかも決められません。ここで例に使う HTTP/1.1 では、ブラウザとサーバーの間でやり取りする用件と付帯情報は、実はそのまま読めるただのテキストです。
送るほうは 1 行目で用件が決まる
ブラウザが送るリクエストは、用件を書いた 1 行目、付帯情報を並べたヘッダー、そして本文の 3 つでできています。
http
GET /articles/network HTTP/1.1
Host: example.com
Accept: text/html1 行目の GET /articles/network が「この住所のものをください」という用件です。ヘッダーはそこに付ける但し書きで、Host はどのドメイン宛てかを示します。ページを取りに行くだけなら本文は空のままです。
返すほうも 1 行目で結果が決まる
サーバーの返事も同じ形です。1 行目に結果、続いてヘッダー、そして本文が入ります。
http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
<!DOCTYPE html>
<html>...</html>200 が結果を表す 3 桁の数字です。この数字は暗記するものではなく、桁で読みます。200 番台は成功、300 番台は転送の知らせ(多くは場所が移ったという案内で、304 は手元に取ってある控えをそのまま使ってよいという意味)、400 番台は依頼した側の間違い、500 番台は受けた側の故障です。
つまり 404 が返っているなら、押しているリンクや叩いている URL のほうを疑います。500 が返っているなら、こちらの送り方は届いていて、サーバーの中で処理が転んでいます。1 桁目を見るだけで、調べる場所が半分に絞れます。
この 2 つのかたまりは、ブラウザの開発者ツールの Network パネルでそのまま読めます。手元から直接見たいときは、端末で小さなサーバーを立て、レスポンスのヘッダーまで表示する指定を付けて取得します。外のサイトには出られない環境でも、自分のマシンの中だけで試せます。
ターミナル
python3 -m http.server 8000 --bind 127.0.0.1 &
SERVER_PID=$!
sleep 1
curl -i http://127.0.0.1:8000/
kill $SERVER_PID1 行目の末尾の & は、サーバーを裏で動かしたまま次の行へ進む印です。--bind 127.0.0.1 で、自分のマシンの中からしかつながらないようにしています。SERVER_PID=$! で今立てたサーバーの番号を控え、sleep 1 でサーバーが待ち受けを始めるまで 1 秒待ってから curl で取得し、最後の kill で控えた番号のサーバーだけを止めます。5 行をまとめて貼ると、curl がつながらずに失敗しても、最後の kill まで進んでサーバーは止まります。失敗したときは、sleep 1 を sleep 3 に替えて待つ時間を延ばし、5 行をもう一度まとめて貼り直します。
返ってくる 1 行目は上の例と少し違い、HTTP/1.0 と出ることがありますが、読み方は同じです。途中に混ざる 127.0.0.1 で始まる行は、サーバーが受け取ったリクエストの記録です。
不具合の報告を受けたら、まずここを見ます。何を送って何が返ったかが分かれば、話は「誰の担当か」から「どこを直すか」に変わります。
HTTP は前回の話を覚えていない
HTTP という決まりは、リクエストを 1 つずつ独立に扱います。さっきログインした人からの 2 回目でも、HTTP の仕組みだけでは前回の話は分かりません。
それでもログイン状態が続くのはなぜか。ログインしたときにサーバーが目印を発行し、ブラウザが Cookie などに入れて、リクエストのたびに送り直しているからです。サーバーは受け取った目印を、手元に持っているログインの記録と照らして、誰からの要求かを思い出します。この「サーバー側で持つ状態」がセッションです。HTTP は覚えませんが、アプリはサーバー側で覚えています。目印そのものに必要な情報を入れて運ぶトークンという方式もあります。サーバーは、自分だけが知っている鍵で目印に署名を付けて渡し、受け取るたびにその署名を確かめます。途中で書き換えられた目印は署名が合わなくなるので見破れます。この方式なら、ログインの記録をサーバーに持たずに済みます。
裏を返すと、Cookie に入れたものは、送り先のドメインやパスの条件に合うリクエストに、ブラウザが自動で付けます。画像 1 枚を取るリクエストでも、条件に合えば付いてきます。Cookie を安易に増やすと、そのサイトへの通信が少しずつ重くなります。トークンを Authorization というヘッダーに入れて送る方式では、ブラウザは自動で付けず、アプリのコードがリクエストごとに入れて送ります。
現場の話
HTTP が覚えないのは不便に見えて、実は台数を増やせる理由になっています。ログインの記録は、どのサーバーからも読める共有の場所に置くか、目印の中に入れて運びます。そうしておけば、どのサーバーが受け取っても判断できるので、前段で好きに振り分けられます。アクセスが増えたらサーバーを足すだけで捌ける設計は、この考え方の上に成り立っています。