HTTP ヘッダとコンテンツ — 何を・どんな形式で
ヘッダは往復書簡の添え書き。宛名の Host、形式と文字コードの Content-Type、量の Content-Length、希望を伝える Accept 系、そしてステートレスの上にセッションを作る Cookie と Set-Cookie を読む。
本文は合っているのに、なぜか化ける・違うものが返る
HTML は正しく書いたはずなのに日本語が「譁�蟄�」のように化ける。API に JSON を送ったのに「形式が不正」と拒否される。同じ URL なのに人によって違う言語のページが出る — こうした「本文は合っているのに」系のトラブルの答えは、たいてい本文ではなくヘッダにあります。
ヘッダは、往復書簡に添えられる 名前: 値 形式の添え書きです。手紙にたとえるなら、宛名・「ワレモノ注意」のラベル・重さの表記・返信用の名札 — 本文そのものではないけれど、本文をどう扱うべきかを決める情報がここに集まっています。3 点セットの 3 つ目「条件と補足」です。
ヘッダは数百種類ありますが、暗記は不要です。この章では「どこ宛か・何がどれだけ・どんな希望で・誰からか」に答える代表 6 つだけを読みます。
ヘッダは本文に添えられたラベルです。宛名(Host)・中身の表示(Content-Type / Content-Length)・希望(Accept 系)・名札(Cookie / Set-Cookie)— 役割で束ねると、数百種類あるヘッダも怖くなくなります。
Host — 1 つの住所に、複数の表札
リクエストの必須ヘッダ Host: example.com は、手紙の宛名です。「IP アドレスに接続しているのだから宛先は自明では?」と思うかもしれませんが、実際には 1 台のサーバ(1 つの IP アドレス)で www.example.com と shop.example.com のように複数のサイトを同居させる運用が普通です。
TCP の接続は建物の玄関までしか案内してくれません。どの部屋(サイト)宛かは、手紙の宛名 — Host ヘッダ — で伝えます。サーバはこれを見て返す内容を切り替えます。これが名前ベースのバーチャルホストで、Host を欠いたリクエストが HTTP/1.1 でエラーになるのは、宛名のない手紙は配れないからです。
Content-Type と Content-Length — 中身は何で、どれだけか
レスポンスの Content-Type: text/html; charset=UTF-8 は「中身は HTML で、文字コードは UTF-8 です」というラベルです。前半の text/html(MIME タイプ)でブラウザは「描画する HTML なのか、表示する画像なのか、ダウンロードさせるファイルなのか」を決め、後半の charset でバイト列をどの文字コードで文字に戻すかを決めます。
ここが文字化けの入口です。実体は UTF-8 で保存されているのに charset=Shift_JIS と宣言すれば、ブラウザは律儀に間違ったルールでバイト列を読み、日本語は化けます。エラーにならず化けたまま表示されるのが厄介なところで、対処は「ラベルと中身を一致させる」の一点です。なお Content-Type はレスポンス専用ではありません — POST で JSON を送るときは、リクエスト側に Content-Type: application/json を付けて中身の形式を伝えます。
Content-Length: 1256 は「ボディは 1256 バイトです」という量の表記です。1 つの TCP 接続を使い回して何通も手紙をやり取りする HTTP/1.1 では、受信側はどこまでが今回のボディかを知る必要があり、この数がその区切りになります。
小問 4-1 — 宛名と中身の添え書き
Host と Content-Type、Content-Length — 「どこ宛か・何が・どれだけ」を読みます。
Q1. 1 台のサーバ(1 つの IP アドレス)で www.example.com と shop.example.com の 2 つのサイトを運用しています。サーバがどちらのサイト宛のリクエストかを判別する手がかりになるヘッダはどれですか。
TCP 接続は IP アドレスに対して張られるため、接続だけではどちらのサイト宛か分かりません。そこでリクエストの Host ヘッダに「www.example.com 宛です」と宛名を書き、サーバはそれを見て返すサイトを切り替えます(名前ベースのバーチャルホスト)。1 つの住所に複数の表札が出ている建物で、手紙の宛名を見て部屋に配る仕組みです。Host が HTTP/1.1 で必須ヘッダなのはこのためです。
Q2. UTF-8 で保存された日本語の HTML を返すレスポンスに Content-Type: text/html; charset=Shift_JIS というヘッダが付いていました。何が起きますか。
ブラウザは原則としてヘッダの宣言を信じてバイト列を解釈します。実体は UTF-8 なのに Shift_JIS と名乗れば、同じバイト列が違う文字に読み替えられて文字化けします。中身とラベルの不一致 — これが文字化けの入口の典型で、直すべきはラベル(charset の宣言)か中身(保存時の文字コード)のどちらかを合わせることです。エラーにはならず、化けたまま表示されてしまうのが厄介な点です。
Q3. レスポンスヘッダ Content-Length: 1256 の意味として正しいものはどれですか。
Content-Length はボディのバイト数です。1 つの接続を続けて使い回す HTTP/1.1 では「どこまでが今回のボディか」を受信側が知る必要があり、この数がその区切りを教えます。行数でもポート番号でもなく、有効期限は次章で扱う Cache-Control の仕事です。開発者ツールの Size 列の値と見比べる場面でも登場します。
Accept 系 — 希望を伝えて、選んでもらう
同じ URL でも、欲しい形は相手によって違います。ブラウザは HTML が欲しいし、プログラムは JSON が欲しい。日本語話者には日本語のページを返したい。この「希望」を伝えるのが Accept 系のリクエストヘッダです。
サーバは提供できる表現の中から選んで返します。これがコンテンツネゴシエーション(内容の交渉)です。ポイントは強制ではなく交渉だということ — 希望が通らないこともあり、実際に何が返ったかはレスポンス側の Content-Type などで確認します。「同じ URL なのに人によって見た目が違う」の種明かしの 1 つがここにあります。
Cookie と Set-Cookie — ステートレスの上にセッションを作る
第 1 章の宿題を回収します。HTTP はステートレスで、サーバは「さっきの続き」を覚えていない。ではログイン状態はどう実現しているのか — 答えは 2 つのヘッダの連携です。
Set-Cookie: session=abc123 を添える。「以後はこの名札を見せてください」Cookie: session=abc123 が付く。サーバは名札を見て「さっきの人だ」と分かるつまりセッションとは、ステートレスな往復書簡の 1 通 1 通に、同じ名札を添え続けることで作られる「続きもの」です。サーバが覚えているのは「名札 abc123 は誰か」という対応表だけで、HTTP 自体は最後までステートレスなまま — この構図が読めると、「Cookie を消したらログアウトされた」「別のブラウザではログインしていない扱いになる」といった日常の現象がすべて説明できます。
小問 4-2 — 希望を伝える・名札を付ける
Accept 系のコンテンツネゴシエーションと、Cookie によるセッションの仕組みです。
Q4. リクエストに Accept: application/json や Accept-Language: ja を付ける「コンテンツネゴシエーション」の説明として正しいものはどれですか。
Accept 系ヘッダは「できれば JSON で」「できれば日本語で」という希望の表明で、サーバは用意できる表現の中から選んで返します(選んだ結果はレスポンスの Content-Type などで分かります)。強制ではなく交渉なので、希望が通らないこともあります。暗号方式の合意は TLS のハンドシェイク、速度調整は TCP の仕事で、HTTP ヘッダの守備範囲ではありません。
Q5. ログインの仕組みとして正しい説明はどれですか。
HTTP はステートレスなので(第 1 章)、サーバは接続を「記憶」しません。代わりにサーバが Set-Cookie で名札(セッション ID など)を渡し、ブラウザが以後のリクエストの Cookie ヘッダに毎回添えることで、サーバは「さっきの人だ」と分かります。パスワードを URL に載せる方式は履歴やログに残る悪手で、IP アドレスは共有・変動があるため識別子になりません。
Q6. Content-Type ヘッダについて正しい説明はどれですか。
Content-Type は「このメッセージのボディの形式はこれです」という添え書きなので、ボディを持つ側なら双方向に付きます。POST/PUT でフォームや JSON を送るときはリクエスト側の Content-Type が頼みごとの解釈を左右し、API がこれを見て 415 などを返すこともあります。HTML 専用ではなく(JSON・画像・PDF 何でも)、text 系では charset で文字コードまで指定します。
この章で持ち帰ること
- ヘッダは往復書簡の添え書き。本文をどう扱うかを決める情報が集まる
- Host は宛名 — 1 つの IP に複数サイトが同居する名前ベースのバーチャルホストを支える必須ヘッダ
- Content-Type(+ charset)はラベル。ラベルと中身の不一致が文字化けの入口。Content-Length はボディのバイト数で、メッセージの区切りを教える
- Accept 系は希望の表明(交渉であって強制ではない)。Set-Cookie → Cookie の名札の往復が、ステートレスの上にセッションを作る
次章はヘッダの応用編にして、Web の速さの心臓部 — キャッシュです。「同じものを二度運ばない」ための条件と補足を、手計算とシミュレータで確かめます。