ブラウザのキャッシュメカニズム
私たちは通常、ブラウザでページを開く際、入力した URL に基づいて対応するサーバーにデータリソースをリクエストします。しかし、このプロセスではページがレンダリングされるまでに一定の待ち時間(ホワイトスクリーンタイム)が発生することがあります。
ユーザーエクスペリエンスを向上させたい場合、さまざまなキャッシュ技術が欠かせません。例えば、DNS キャッシュ、CDN キャッシュ、ブラウザキャッシュ、ページのローカルキャッシュなどがあります。適切なキャッシュ戦略を導入することで、重複したリソースリクエストを減らし、サーバーの負荷を軽減し、ページのロード速度を向上させることができます。
本記事では、HTTP の強制キャッシュと協調キャッシュについて説明します。
基本原理
ブラウザがリソースを読み込む際、最初にリクエストヘッダーの Expires
や Cache-Control
の値を基に、強制キャッシュが適用されるかどうかを判断します。キャッシュが有効であれば、リモートサーバーにリクエストせずにローカルキャッシュを使用します。
強制キャッシュ(強キャッシュ)
ブラウザの強制キャッシュには、Expires
(HTTP/1.0 規格)と Cache-Control
(HTTP/1.1 規格)の 2 種類があります。
Expires(有効期限)
Expires
は HTTP/1.0 の規格で、リソースの有効期限を示すヘッダーフィールドです。この値はサーバー側で設定され、絶対時間で指定されます。
ブラウザが最初にリソースをリクエストすると、サーバーのレスポンスヘッダーに Expires
が含まれます。その後、ブラウザが同じリソースを再リクエストすると、前回の Expires
の値を確認し、まだ有効期限内であればキャッシュを使用します。
ただし、Expires
はクライアントのローカル時間を基準にするため、クライアントとサーバーの時間がずれている場合、キャッシュの判定に誤差が生じる可能性があります。
Cache-Control(キャッシュ制御)
上記の Expires
には、クライアントとサーバーの時間のズレによる誤差という欠点があります。これを解決するために、HTTP/1.1 では Cache-Control
が導入されました。Cache-Control
の優先度は Expires
より高く、キャッシュの有効期間を相対時間で指定できます。
Cache-Control
には以下のような主要なディレクティブ(指示)が含まれます:
-
max-age
:例えばmax-age=3600
の場合、現在の時間+ 3600 秒の間、新しいリソースをリクエストしません。 -
s-maxage
:max-age
に似ていますが、プロキシサーバーのキャッシュ時間を指定するためのものです。 -
private
:キャッシュをクライアント側のみに保存し、プロキシサーバーには保存しません。 -
public
:クライアントとプロキシサーバー両方がキャッシュ可能。 -
no-store
:データを一切キャッシュしない。 -
no-cache
:キャッシュには保存するが、リソースを使用する前に必ずオリジンサーバーで検証する。
協調キャッシュ(協商キャッシュ)
強制キャッシュはローカルブラウザ側でキャッシュを管理しますが、キャッシュが無効になった場合はサーバーへリクエストを送信し、協調キャッシュの判定を行います。もしキャッシュが有効ならば、HTTP ステータスコード 304(Not Modified)が返され、新しいリソースデータは送信されません。
協調キャッシュ(比較キャッシュとも呼ばれる)は、サーバー側でリソースの有効性を判定する仕組みです。これには、以下の 2 つのフィールドペアが使用されます。
-
Last-Modified
/If-Modified-Since
-
ETag
/If-None-Match
初回リクエスト時に、サーバーは Last-Modified
または ETag
をレスポンスヘッダーに含めます。次回のリクエスト時には、ブラウザは以下のリクエストヘッダーを送信します。
-
Last-Modified
:リソースの最終更新日時をサーバーが返す。 -
If-Modified-Since
:クライアントが以前受信したLast-Modified
の値をサーバーに送信し、それが変更されていないか確認。 -
ETag
:ファイルの一意の識別子(ハッシュ値など)。リソースの内容が変更されると、新しいETag
に更新される。 -
If-None-Match
:前回受信したETag
をリクエストヘッダーに含め、現在のリソースと一致するか確認。
サーバーは ETag
または If-Modified-Since
の値をチェックし、変更がなければ304 Not Modifiedを返し、クライアントはローカルキャッシュを使用します。
強制キャッシュと協調キャッシュの具体的なフロー
-
ブラウザがリソースをリクエスト
- ローカルにキャッシュがなければ、サーバーから新しいリソースを取得し、
Last-Modified
やETag
を保存。
- ローカルにキャッシュがなければ、サーバーから新しいリソースを取得し、
-
キャッシュが存在する場合
- 強制キャッシュ(
Cache-Control
>Expires
)の判定 - 有効期間内ならローカルキャッシュを使用(ステータスコード 200)。
- 期限切れの場合、協調キャッシュの検証を実施。
- 強制キャッシュ(
-
協調キャッシュの検証
-
ETag
が存在し、サーバーのETag
と一致すれば、304 Not Modified を返し、キャッシュを再利用。 -
ETag
がなければ、If-Modified-Since
とLast-Modified
を比較し、一致すれば 304 Not Modified を返す。 -
ETag
やLast-Modified
が不一致なら、新しいリソースを取得。
-
なぜ ETag
が必要なのか?
ETag
は、Last-Modified
のいくつかの問題を解決するために導入されました。
-
ファイル内容が変更されていないのに最終更新日時が変わることがある
- 例えば、サーバー上でファイルをコピー&ペーストした場合、ファイルの更新日時が変わる可能性があります。これにより、クライアントは「リソースが変更された」と誤認し、不要なリクエストが発生します。
-
ファイルの変更が頻繁に行われる場合、
Last-Modified
の秒単位の精度では対応できない-
If-Modified-Since
は秒単位でしか変更を検知できないため、1 秒以内に複数回変更が発生する場合、正しく判定できません。 -
ETag
なら、より細かい変更の追跡が可能です。
-
-
一部のサーバーではファイルの最終更新日時を正確に取得できない
- 例えば、動的に生成されるファイルなどでは、
Last-Modified
の管理が難しい場合があります。ETag
を使えば、サーバー側でリソースの一意性を保証できます。
- 例えば、動的に生成されるファイルなどでは、
ステータスコードの違い
- 200:通常の成功レスポンス。サーバーが新しいリソースを返す。
- 200 (from memory cache / from disk cache):強制キャッシュが有効な場合。ブラウザのローカルキャッシュを使用する。
- 304 Not Modified:協調キャッシュが適用され、リソースの再取得が不要と判定された場合。
補足:
-
from memory cache
はページのリフレッシュ時にメモリから取得されたことを示す。 -
from disk cache
はタブを閉じた後、ディスクキャッシュから取得されたことを示す。
キャッシュの優先順位
キャッシュの適用順序は以下のようになります。
-
Cache-Control
>Expires
-
Cache-Control
がある場合、Expires
は無視される。 - つまり、
Cache-Control
の方が優先度が高い。
-
-
強制キャッシュ > 協調キャッシュ
- 強制キャッシュが有効な場合、協調キャッシュの検証は行われない。
- つまり、まず強制キャッシュを確認し、期限切れなら協調キャッシュを適用する。
-
ETag
>Last-Modified
- 協調キャッシュの判定時、
ETag
がある場合はLast-Modified
を無視する。 - つまり、
ETag
の方が優先度が高い。
- 協調キャッシュの判定時、
追加情報
HTTP/1.0 では、Pragma
というキャッシュ制御ヘッダーが使用されていました。これは Cache-Control: no-cache
と同じ効果を持ち、リソースを再検証するよう強制します。しかし、HTTP/1.1 では Cache-Control
が導入され、Pragma
はほぼ廃止されました。
キャッシュ適用の優先順位は以下のようになります:
Pragma → Cache-Control → Expires → ETag → Last-Modified
ヒューリスティックキャッシュ(推測キャッシュ)
サーバーが Cache-Control
や Expires
を設定していない場合、ブラウザはヒューリスティックキャッシュという独自のキャッシュ戦略を適用することがあります。これは、Last-Modified
の値を基にキャッシュの有効期間を推測する方式です。
例えば、以下のような計算が適用されることがあります:
(現在の時刻 - Last-Modified の時刻) × 0.1
つまり、リソースが更新されてから 10%の期間をキャッシュ有効期間として推測する、というルールです。
ただし、この戦略はサーバーのキャッシュポリシーに依存せず、ブラウザの独自の判断で行われるため、一貫性に欠けることがあります。
参考:
HTTP Heuristic Caching (Missing Cache-Control and Expires Headers) Explained
その他の補足
-
協調キャッシュは強制キャッシュと組み合わせて使用するのが基本
- 強制キャッシュを無効にしてしまうと、協調キャッシュだけではあまり意味がありません。
-
ほとんどの Web サーバーでは、デフォルトで協調キャッシュ(
Last-Modified
とETag
)が有効になっている- つまり、特に設定しなくても協調キャッシュが機能している場合が多い。
注意すべきケース
1. 分散システム(クラスタリング環境)での Last-Modified
の一貫性
-
Last-Modified
の値が異なるサーバー間で統一されていないと、キャッシュが適切に機能しない可能性があります。 - 例えば、異なるサーバーでレスポンスを処理する際、最終更新日時がバラバラだと、ブラウザは毎回キャッシュを無効化して新規リクエストを送信することになります。
2. 分散システムでは ETag
の使用を避けるべき
-
ETag
はサーバーごとに異なる値が生成されるため、負荷分散環境ではキャッシュが正しく適用されないことがあります。 - そのため、クラスタリング環境では
ETag
を無効化するのが一般的です。
私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。
Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:
複数言語サポート
- Node.js、Python、Go、Rustで開発できます。
無制限のプロジェクトデプロイ
- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。
比類のないコスト効率
- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。
洗練された開発者体験
- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。
簡単なスケーラビリティと高パフォーマンス
- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。
Xでフォローする:@LeapcellHQ