- ネットワーク
- 技術 💻
HTTPの誤解8選|no-cache・401と403・CORSプリフライト
HTTPって、動いて当たり前すぎて、仕様を読む機会がないんですよね。でも、キャッシュ、認証、CORS、暗号化。「知らなかった」がそのまま障害や設定ミスになるのが、この領域です。
今回は、知っていると一目置かれるものを8つ集めました。毎日使っているのに、実は知らないことが一番多いプロトコルです。1つでも知らなかったら儲けものです。異論・追加も大歓迎です。
① HTTPSでも、どこに繋いだかは見えています
「HTTPSだから、通信はぜんぶ暗号化されている」——半分だけ正解です。通信の中身は暗号化されます。でも、接続先のドメイン名は、暗号化が始まる前に平文で流れているんです。
TLSの接続開始時、クライアントは「どのサイトに繋ぎたいか」をSNIという仕組みで名乗ります。1つのサーバーが複数サイトを載せているので、暗号化の準備より先に行き先を伝える必要があるんですよね。だから経路上からは、「何を見たか」は分からなくても「どこを見たか」は分かります。
これを隠すECH(Encrypted Client Hello)という仕組みの普及が、いままさに進んでいるところです。
② no-cacheは、「キャッシュするな」ではありません
Cache-Controlの指定で、いちばん誤解されているやつです。no-cacheは「保存はしていい。ただし使う前に毎回サーバーに確認しろ」という意味です。「保存するな」は、no-storeの方なんですよね。
Cache-Control: no-cache → 保存OK・毎回再検証
Cache-Control: no-store → 保存そのものを禁止
だから機密情報のレスポンスにno-cacheだけ付けても、ディスクには残ります。逆に、更新をすぐ反映させたいだけなのにno-storeを付けると、キャッシュの恩恵をぜんぶ捨てることになります。名前と意味がズレている、設定ミスの名所です。
③ HTTPは、もうテキストですらありません
「HTTPはテキストプロトコル」——HTTP/1.1までの話です。HTTP/2からは、中身がバイナリのフレーム形式になりました。あの GET / HTTP/1.1 という読める形式は、ワイヤ上にはもう流れていません。
そしてHTTP/3では、さらに踏み込んでTCPを捨てました。UDP上に構築されたQUICというプロトコルの上で動きます。パケットロス時に他のリクエストまで巻き込んで止まる、TCPの構造的な弱点を避けるためなんですよね。
curlやDevToolsがテキスト風に見せてくれているだけで、実体はとっくに別物になっているんです。
④ 401は「認証していない」、403は「認証しても駄目」
ステータスコードの使い分けで、いちばん事故が多いペアです。
401 Unauthorizedの実際の意味は、「未認証」です。「誰だか分からないので、名乗ってください」ですね。だからレスポンスには「どう名乗ればいいか」(WWW-Authenticate)を付けるのが仕様です。一方、403 Forbiddenは「誰だか分かったうえで、駄目」。再ログインしても結果は変わりません。
Unauthorizedという名前が「認可されていない」に見えるのが混乱の元なんですよね。401なら再ログインへ誘導、403ならエラー表示。クライアントの挙動が分かれるので、ここを逆にすると使う側が困るんです。
⑤ GETにボディを付けることは、禁止されていません
「GETにリクエストボディは付けられない」——実は、仕様では禁止されていないんです。正確には、「GETのボディに意味は定義されていない」という書き方になっています。送ること自体はできるが、サーバーがどう扱うかは決まっていない。そして経路上のプロキシが落とすかもしれない。
実際、検索エンジンのElasticsearchは、複雑な検索条件をGETのボディで受け付けてきました(POSTでも同じことができます)。「禁止」と「未定義」は違う。仕様の言葉づかいを正確に読むと、議論が一段深くなる例です。
⑥ Refererは、スペルミスのまま30年です
リクエストヘッダの Referer、正しい英語はreferrer(rが3つ)です。仕様策定時のつづり間違いが、互換性のためにそのまま標準になりました。世界中のサーバーとブラウザが、間違ったつづりで通信し続けているんですよね。
さらに味わい深いのがここで。後から作られた関連ヘッダの Referrer-Policy は、正しいつづりで標準化されました。つまりいまのWebには、間違ったRefererと正しいReferrer-Policyが同居しています。ヘッダ名を手打ちするとき、どちらの流儀か毎回思い出すことになるんです。
⑦ OPTIONSリクエストは、ブラウザが勝手に送っています
自分では一度も書いたことがないのに、本番のログに大量に残っているOPTIONS。CORSのプリフライトです。
ブラウザは、別オリジンへの「単純ではないリクエスト」を送る前に、「これ、送っていいですか?」とOPTIONSで先に確認します。本命のリクエストは、サーバーが許可を返した後にしか飛びません。
面白いのが、飛ぶかどうかの境界線です。Content-Typeが application/x-www-form-urlencoded なら単純扱いでそのまま飛ぶのに、application/json にした瞬間、プリフライトが発生します。フォーム時代からの互換ラインが基準になっているんですよね。「同じAPIに2回リクエストが来ている?」の正体、だいたいこれです。
⑧ HTTP/2のサーバープッシュは、もう亡くなりました
「サーバーが先回りしてリソースを送ってくれる」——HTTP/2最大の目玉として登場したサーバープッシュ、覚えていますか。主要ブラウザから、すでに削除されています。
クライアントが持っているキャッシュをサーバーは知らないので、すでに持っているものまで押し付けて、かえって無駄が出る。実測で効果が出なかったんですよね。
代わりの立ち位置にいるのが、103 Early Hints。「本命のレスポンスを作っている間に、先に読み込んでおくといいURLだけ教える」という控えめな方式です。押し付けるのをやめて、ヒントだけ渡す形に落ち着いたんです。鳴り物入りの機能が消えて、地味な方式が残る。プロトコルの世界でも、あるあるなんですね。
番外編:実在する、変なステータスコード
418 I'm a teapot
「私はティーポットなので、コーヒーは淹れられません」。エイプリルフールのジョークRFC(コーヒーポット制御プロトコル)由来ですが、本物のRFCとして実在します。後年、この番号を実務に転用しようとする動きが出たときは、世界中の開発者が「418を守れ」と声を上げました。
451 Unavailable For Legal Reasons
法的理由(検閲や裁判所命令)でコンテンツを出せないときのコード。番号の由来は、焚書を描いた小説『華氏451度』です。仕様書に文学が入り込んでいる、粋な例です。
手元で確かめる
curl -sI https://example.com
-I でヘッダだけ取得できます。今日の話の多くは、この1行で観察できます。
RJCでも、HTTPと向き合っています
RJCが携わる保険、金融、人材、決済、交通のシステムは、外部サービスとのAPI連携が当たり前にあります。この世界では、ステータスコードの解釈違いや、キャッシュ制御の設定ひとつが、そのまま障害報告になるんですよね。
だから今日のような話を、Techチームで共有しながら開発しています。仕様を正確に知っていることが、そのまま品質になる領域なんです。
よくいただくご質問
Q. まずどれから押さえるべきですか?
A. ②のno-cacheかなと思います。今日書く設定にそのまま効きますし、誤解している人が本当に多いんですよね。
Q. ステータスコードは、全部暗記すべきですか?
A. 全部は要りません。実務で会う20個ほどと、「4xxは呼んだ側、5xxは呼ばれた側の問題」という責任の分界を押さえれば十分です。
Q. HTTP/3って、対応作業が必要ですか?
A. 多くの場合、CDNやロードバランサ側で有効化する話で、アプリのコードはほぼそのままです。「気づいたら使っていた」が一番多いパターンなんですよね。
Q. 未経験からでも、ここまで分かるようになりますか?
A. なります。HTTPはcurlとブラウザのDevToolsで全部観察できるので、手を動かした分だけ確実に分かるようになります。
全員が使っていて、ほとんど誰も読んでいない
今日の8つを並べてみて思ったのが、これでした。HTTPは、エンジニア全員が毎日使っています。でも仕様を読んだことがある人は、ごくわずか。
だからこそ、正確に知っているだけで差がつく、コスパのいい領域なのかなと思います。
みなさんがHTTPで踏んだ勘違いや事故、よかったら教えてください。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。