0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HTTPキャッシュの理解:強制キャッシュと協調キャッシュ

Posted at

表紙

ブラウザのキャッシュメカニズム

私たちは通常、ブラウザでページを開く際、入力した URL に基づいて対応するサーバーにデータリソースをリクエストします。しかし、このプロセスではページがレンダリングされるまでに一定の待ち時間(ホワイトスクリーンタイム)が発生することがあります。

ユーザーエクスペリエンスを向上させたい場合、さまざまなキャッシュ技術が欠かせません。例えば、DNS キャッシュ、CDN キャッシュ、ブラウザキャッシュ、ページのローカルキャッシュなどがあります。適切なキャッシュ戦略を導入することで、重複したリソースリクエストを減らし、サーバーの負荷を軽減し、ページのロード速度を向上させることができます。

本記事では、HTTP の強制キャッシュ協調キャッシュについて説明します。

基本原理

ブラウザがリソースを読み込む際、最初にリクエストヘッダーの ExpiresCache-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-maxagemax-age に似ていますが、プロキシサーバーのキャッシュ時間を指定するためのものです。
  • private:キャッシュをクライアント側のみに保存し、プロキシサーバーには保存しません。
  • public:クライアントとプロキシサーバー両方がキャッシュ可能。
  • no-store:データを一切キャッシュしない
  • no-cacheキャッシュには保存するが、リソースを使用する前に必ずオリジンサーバーで検証する

協調キャッシュ(協商キャッシュ)

強制キャッシュはローカルブラウザ側でキャッシュを管理しますが、キャッシュが無効になった場合はサーバーへリクエストを送信し、協調キャッシュの判定を行います。もしキャッシュが有効ならば、HTTP ステータスコード 304(Not Modified)が返され、新しいリソースデータは送信されません。

協調キャッシュ(比較キャッシュとも呼ばれる)は、サーバー側でリソースの有効性を判定する仕組みです。これには、以下の 2 つのフィールドペアが使用されます。

  1. Last-Modified / If-Modified-Since
  2. 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を返し、クライアントはローカルキャッシュを使用します。

強制キャッシュと協調キャッシュの具体的なフロー

  1. ブラウザがリソースをリクエスト

    • ローカルにキャッシュがなければ、サーバーから新しいリソースを取得し、Last-ModifiedETag を保存。
  2. キャッシュが存在する場合

    • 強制キャッシュ(Cache-Control > Expires)の判定
    • 有効期間内ならローカルキャッシュを使用(ステータスコード 200)。
    • 期限切れの場合、協調キャッシュの検証を実施。
  3. 協調キャッシュの検証

    • ETag が存在し、サーバーの ETag と一致すれば、304 Not Modified を返し、キャッシュを再利用。
    • ETag がなければ、If-Modified-SinceLast-Modified を比較し、一致すれば 304 Not Modified を返す。
    • ETagLast-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 はタブを閉じた後、ディスクキャッシュから取得されたことを示す。

キャッシュの優先順位

キャッシュの適用順序は以下のようになります。

  1. Cache-Control > Expires

    • Cache-Control がある場合、Expires は無視される。
    • つまり、Cache-Control の方が優先度が高い
  2. 強制キャッシュ > 協調キャッシュ

    • 強制キャッシュが有効な場合、協調キャッシュの検証は行われない
    • つまり、まず強制キャッシュを確認し、期限切れなら協調キャッシュを適用する
  3. 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-ControlExpires を設定していない場合、ブラウザはヒューリスティックキャッシュという独自のキャッシュ戦略を適用することがあります。これは、Last-Modified の値を基にキャッシュの有効期間を推測する方式です。

例えば、以下のような計算が適用されることがあります:

(現在の時刻 - Last-Modified の時刻) × 0.1

つまり、リソースが更新されてから 10%の期間をキャッシュ有効期間として推測する、というルールです。

ただし、この戦略はサーバーのキャッシュポリシーに依存せず、ブラウザの独自の判断で行われるため、一貫性に欠けることがあります。

参考:
HTTP Heuristic Caching (Missing Cache-Control and Expires Headers) Explained

その他の補足

  • 協調キャッシュは強制キャッシュと組み合わせて使用するのが基本

    • 強制キャッシュを無効にしてしまうと、協調キャッシュだけではあまり意味がありません。
  • ほとんどの Web サーバーでは、デフォルトで協調キャッシュ(Last-ModifiedETag)が有効になっている

    • つまり、特に設定しなくても協調キャッシュが機能している場合が多い。

注意すべきケース

1. 分散システム(クラスタリング環境)での Last-Modified の一貫性

  • Last-Modified の値が異なるサーバー間で統一されていないと、キャッシュが適切に機能しない可能性があります。
  • 例えば、異なるサーバーでレスポンスを処理する際、最終更新日時がバラバラだと、ブラウザは毎回キャッシュを無効化して新規リクエストを送信することになります。

2. 分散システムでは ETag の使用を避けるべき

  • ETag はサーバーごとに異なる値が生成されるため、負荷分散環境ではキャッシュが正しく適用されないことがあります。
  • そのため、クラスタリング環境では ETag を無効化するのが一般的です。

私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。

Leapcell

Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:

複数言語サポート

  • Node.js、Python、Go、Rustで開発できます。

無制限のプロジェクトデプロイ

  • 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

比類のないコスト効率

  • 使用量に応じた支払い、アイドル時間は課金されません。
  • 例: $25で6.94Mリクエスト、平均応答時間60ms。

洗練された開発者体験

  • 直感的なUIで簡単に設定できます。
  • 完全自動化されたCI/CDパイプラインとGitOps統合。
  • 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

簡単なスケーラビリティと高パフォーマンス

  • 高い同時実行性を容易に処理するためのオートスケーリング。
  • ゼロ運用オーバーヘッド — 構築に集中できます。

ドキュメントで詳細を確認!

Try Leapcell

Xでフォローする:@LeapcellHQ


ブログでこの記事を読む

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?