6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

「キャッシュが残ってて最新のデータが見えない」ことや「ユーザごとに違うはずのページが他の人に見えてしまった」など,キャッシュによる意図しない挙動に頭を悩ませた経験があったため簡単にまとめてみました.

この記事では,IIJ Engineers Blogの解説記事をベースに,HTTPのキャッシュ制御とプロキシの役割について整理しています.こちらの記事は図などもおおく非常にわかりやすいのでおすすめです!(その分ボリュームがあるため,短いバージョンを本記事で作成します)

用語の整理

用語 説明
ブラウザ Webページを見るためのアプリケーション
Webサーバ HTMLなどのコンテンツを提供するサーバ
HTTPクライアント Webサーバにアクセスする側.今回はブラウザ
プロキシ 通信を中継・代理するサーバ.通信の制御やキャッシュで使用
フォワードプロキシ クライアントの代理.社内LAN → インターネットなど.内→外
リバースプロキシ サーバの代理.CDNやロードバランサが該当.外→内

キャッシュの基本

現代のWebページはリッチ化の一途をたどっており,通信速度が高速になったとしてもリソースを逐一ダウンロードしていては時間がかかってしまいます.それを防ぐために,すでにダウンロード済みのものを利用するという高速化技術をキャッシュと言います.これはブラウザやプロキシ等で行われます.
さらにキャッシュにリソースを保持しておく時間的な長さのことをキャッシュ期間といいます.


ここで,キャッシュに存在するトレードオフを紹介します.

キャッシュ期間を長くすると...

  • ✅ 重複したリソースのダウンロードやWebサーバとの通信量が減り,表示が速くなる
  • ❌ 古いデータが残ってしまう可能性がある

キャッシュ期間を短くすると...

  • ✅ 常に最新の情報を取得できる
  • ❌ 通信が増えたり,キャッシュの意味がなくなる

理想としては,リソースが更新された時だけダウンロードを行い,キャッシュを常に新しい状態に保つことですよね.
これを実現するために行われるのが「キャッシュの検証」です.

キャッシュの検証と 304 Not Modified

キャッシュの検証は,ブラウザがサーバに「このキャッシュ,まだ使っていい?」と確認する仕組みです.

サーバの返事が「変更なし」なら...

  • ステータスコード 304 Not Modified が返る
  • ブラウザはキャッシュをそのまま再利用
  • 同じデータを再度ダウンロードせず,効率的!

304応答の仕組み:2つの方式

1. 更新時間での検証:If-Modified-Since ヘッダ

  • サーバ側: Last-Modified ヘッダで最終更新日時を返す
  • ブラウザ側: 次回リクエスト時の If-Modified-Since に最終更新日時を付ける
  • 比較結果: 変更がなければ 304,あれば新データを送信

2. 識別子(ETag)での検証:If-None-Match ヘッダ

  • サーバ側: ETag ヘッダで識別子を返す
  • ブラウザ側: 次回リクエスト時の If-None-MatchETag ヘッダを付ける
  • 一致すれば 304,不一致なら新しいデータを送る

If-Modified-SinceIf-None-Match の両方がある場合は,ETagが優先されます!
柔軟に(広告とブログ間でキャッシュ期間を分けるみたいな)一致条件を制御できるため

プロキシとキャッシュの注意点

ブラウザキャッシュとプロキシキャッシュは別物

キャッシュは基本的にブラウザとプロキシで行われます.この2つは同じに見えますが, 別物として管理する必要があります.
なぜならプロキシキャッシュは複数のユーザに利用されることが多いからです.

  • ブラウザキャッシュは個人利用で,利用者本人が管理
  • プロキシキャッシュは複数人利用で,プロキシ管理者が管理

例えばクレジットカード番号や認証後のマイページなど,他人に見られたくないページはプロキシにキャッシュさせてはいけません!

したがってキャッシュをどこで扱うかコントロールする必要があります.
それがCache-Controlヘッダです.

Cache-Control ヘッダの基本

キャッシュの挙動は,Cache-Control ヘッダで細かく指定できます.
主にリクエスト時とレスポンス時の2種類に分けられます.

リクエスト時の指定

ブラウザからサーバへリクエストを送る際,すでに保存されているキャッシュを使うかどうか,クライアント側が指定できます!
特にCSSなどが新しく反映されないときにスーパーリロードを使って最新データを強制取得する動作などで使われます.

ディレクティブ 説明
no-cache 検証はするがキャッシュは信用しない(max-age=0と同等)
max-age=60 60秒以内のキャッシュは使ってもよい

no-cacheはキャッシュを使わないって意味じゃないの?

よく勘違いしがちなのですが,no-cacheは(正確には)キャッシュを使わないって意味ではありません.

1.リクエスト時はキャッシュを利用せず,Webサーバへ問い合わせます
2-1. レスポンスが200であり,キャッシュに保存されているものよりも新しいリソースに更新されている場合はそれをレスポンスとして返します
2-2. レスポンスが304ならキャッシュに保存されているものを利用するのです

一切キャッシュを利用したくなければno-storeを使いましょう

レスポンス時の指定

サーバからブラウザへレスポンスを返す際,かなり複雑で数も多く,状況によってディレクティブの意味が変わります.そのため,キャッシュの再利用に関わる動作だけに注目して解説します.

指定 対象 説明
no-store ブラウザとプロキシ 一切キャッシュさせない(履歴にも残さない)
private プロキシ ユーザ単位のキャッシュのみ許可(プロキシには不可)
public プロキシ 誰が見ても問題ない内容.プロキシ共有可
must-revalidate ブラウザとプロキシ キャッシュが切れたら再検証必須
max-age=300 ブラウザとプロキシ キャッシュの有効期限(秒)
s-maxage=600 プロキシ プロキシ用キャッシュの有効期限

キャッシュの再利用が決定される時

  1. 既存キャッシュに対して,キャッシュ更新の検証は必要か
  2. 検証が不要な場合,キャッシュは新鮮か
  3. 接続失敗時,キャッシュを使用するか

「検証は必要か?」

  • 「検証する」= キャッシュは使うが,サーバに確認をとる(If-Modified-Since や ETag)
  • no-cacheno-store があると検証が強制される
  • キャッシュが更新されて,最新データが表示される
  • つまりキャッシュを利用していないことと同等

「キャッシュは新鮮か?」

  • キャッシュ期間を超えて古くなっていないかを,サーバ接続前に判断する処理(検証なしでキャッシュを活用するか判断する処理)

「接続失敗時,キャッシュを利用するか?」

  • 認証失敗もキャッシュ利用してしまうので,情報漏洩につながる危険性あり
  • no-storeなどを利用して強制的にキャッシュさせないことも必要

具体例:キャッシュしつつ安全に情報を扱いたい場合

ユースケース

  • 会員マイページのように更新頻度は低い
  • ブラウザにはキャッシュしてほしい
  • プロキシにはキャッシュしてほしくない
  • 認証失敗時に古い情報を出したくない

推奨ヘッダ

Cache-Control: max-age=300, private, must-revalidate

最後に

キャッシュについてまとめました.プロキシやWebブラウザでキャッシュが行われていることはなんとなくわかっていたつもりでしたが,キャッシュ制御の必要性を考慮する必要があるということは目から鱗でした.
HTTPには他にも様々な高速化技術が組み込まれているため今後も勉強していきます.

参考

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?