キャッシュを大切に ❤️

サイトを 2 度目に読み込んだユーザーは HTTP キャッシュを使用するため、適切に機能することを確認してください。

この投稿は、Chrome Dev Summit 2020 の拡張コンテンツの一部である「Love your cache」の動画に付随するものです。ぜひ、こちらの動画をご覧ください。

ユーザーがサイトを 2 回目に読み込むと、ブラウザは HTTP キャッシュ内のリソースを使用して、読み込みを高速化します。ただし、ウェブのキャッシュに関する標準は 1999 年に定められたものであり、かなり広範に定義されています。CSS や画像などのファイルをネットワークから再度取得するか、キャッシュから読み込むかを判断するのは、やや不確実な作業です。

この記事では、キャッシュに適した最新のデフォルトについて説明します。これは、実際にはキャッシュをまったく使用しません。ただし、これはデフォルトの設定にすぎません。もちろん、単に「オフにする」よりも微妙な違いがあります。ご一読ください。

目標

サイトが 2 回目に読み込まれるときには、次の 2 つの目標があります。

  1. ユーザーが最新バージョンを利用できるようにします。変更を加えた場合は、すぐに反映されるようにします。
  2. ネットワークからできるだけ少ないデータを取得しながら #1 を行う

広い意味では、サイトを再度読み込んだときに、クライアントに送信する変更は最小限に抑えるべきです。また、変更を最も効率的に配信できるようにサイトを構成することは困難です(詳しくは下記と動画をご覧ください)。

ただし、キャッシュを検討する際には、他の調整要素もあります。たとえば、ユーザーのブラウザ HTTP キャッシュにサイトを長時間保持し、配信にネットワーク リクエストを必要としない場合があります。または、サイトが最新かどうかを確認する前に、完全にオフラインでサイトを提供する Service Worker を構築したとします。これは極端なオプションですが、有効であり、多くのオフライン ファーストのアプリのようなウェブ エクスペリエンスで使用されています。ただし、ウェブはキャッシュのみの極端な状態である必要はなく、ネットワークのみの極端な状態である必要もありません。

背景

ウェブ デベロッパーは、キャッシュが「古くなる」という考えに慣れています。しかし、この問題を解決するためのツールは、ほとんど本能的に知っています。「強制更新」を行うか、シークレット ウィンドウを開くか、ブラウザのデベロッパー ツールを組み合わせてサイトのデータを消去します。

一般のインターネット ユーザーには、そのような余裕はありません。そのため、2 回目の読み込みでユーザーが快適に過ごせるようにすることが Google の主要な目標ですが、不快な思いをしたり、動作が停止したりしないようにすることも非常に重要です。(web.dev/live サイトがクラッシュしそうになったときの様子については、動画をご覧ください)

背景を説明すると、「古いキャッシュ」の一般的な原因は、1999 年当時のデフォルトのキャッシュ設定です。これは Last-Modified ヘッダーに依存します。

ユーザーのブラウザでさまざまなアセットがキャッシュされる期間を示す図
異なる時間に生成されたアセット(グレー)は異なる時間にキャッシュに保存されるため、2 回目の読み込みではキャッシュに保存されたアセットと新しいアセットが組み合わせて取得されます

読み込まれたファイルは、ブラウザが認識している現在の有効期間の 10% 追加で保持されます。たとえば、index.html が 1 か月前に作成された場合、ブラウザによってさらに 3 日間ほどキャッシュに保存されます。

これは当時は善意に基づくアイデアでしたが、今日のウェブサイトは緊密に統合されているため、このデフォルトの動作では、ウェブサイトの異なるリリース(火曜日のリリースの JS と金曜日のリリースの CSS など)用に設計されたファイルがユーザーに提供される可能性があります。これは、これらのファイルがまったく同じタイミングで更新されていないためです。

明るい小道

キャッシュのデフォルト設定は、実際にはキャッシュをまったく使用せず、CDN を使用してコンテンツをユーザーの近くに配置することです。ユーザーがサイトを読み込むたびに、ネットワークにアクセスして最新の状態かどうかを確認します。このリクエストは、各エンドユーザーに地理的に近い CDN から提供されるため、レイテンシは低くなります。

ウェブリクエストにこのヘッダーで応答するようにウェブホストを構成できます。

Cache-Control: max-age=0,must-revalidate,public

つまり、このファイルは有効期間がないため、再度使用する前にネットワークから検証する必要があります(検証しないと「推奨」のみとなります)。

この検証プロセスは、転送されるバイト数に比べて比較的低コストです。サイズの大きな画像ファイルが変更されていない場合、ブラウザは小さな 304 レスポンスを受信します。ただし、ユーザーがネットワークにアクセスして確認する必要があるため、レイテンシが発生します。これが、このアプローチの主なデメリットです。これは、先進国で高速接続を使用しているユーザーや、選択した CDN が広範囲にカバーしているユーザーには非常に効果的ですが、低速のモバイル接続を使用しているユーザーや、インフラストラクチャが貧弱なユーザーには適していません。

いずれにせよ、これは一般的な CDN である Netlify のデフォルトである最新のアプローチですが、ほぼすべての CDN で構成できます。Firebase Hosting の場合、このヘッダーを firebase.json ファイルの hosting セクションに含めることができます。

"headers": [
  // Be sure to put this last, to not override other headers
  {
    "source": "**",
    "headers": [ {
      "key": "Cache-Control",
      "value": "max-age=0,must-revalidate,public"
    }
  }
]

そのため、この値をデフォルトとして使用することをおすすめしますが、あくまでもデフォルトです。ここでは、デフォルトの設定にアップグレードしてアップグレードする方法を紹介します。

フィンガープリント付き URL

サイト上で提供されるアセットや画像などのファイル名にファイルの内容のハッシュを含めると、これらのファイルに常に一意のコンテンツが含まれるようにできます。たとえば、ファイル名が sitecode.af12de.js になります。サーバーがこれらのファイルのリクエストに応答するときに、このヘッダーで構成することで、エンドユーザーのブラウザに長時間キャッシュに保存するよう安全に指示できます。

Cache-Control: max-age=31536000,immutable

この値は 1 年を秒単位で表します。仕様では 実質的に これは“永久”に等しくなります

重要なのは、これらのハッシュを手作業で生成しないことです。手作業が多すぎるからです。この作業には、Webpack や Rollup などのツールを使用できます。詳しくは、ツール レポートをご覧ください。

なお、指紋付き URL のメリットを享受できるのは JavaScript だけではありません。アイコン、CSS、その他の不変のデータファイルなどのアセットも、この方法で名前を付けることができます。(サイトの変更時にコードの量を減らすことができるコード分割について詳しくは、上の動画をご覧ください)。

サイトのキャッシュ保存方法にかかわらず、このような指紋付きファイルは、構築するサイトにとって非常に価値があります。ほとんどのサイトは、リリースごとに変更されることはありません。

もちろん、ユーザー向けの「わかりやすい」ページの名前をこのように変更することはできません。index.html ファイルを index.abcd12.html に変更するなど、現実的ではありません。サイトを読み込むたびに新しい URL に移動するようユーザーに伝えることはできません。これらの「わかりやすい」URL は、この方法で名前を変更してキャッシュに保存することはできません。そこで、妥協案を検討します。

中間地点

キャッシュに関しては、明らかに中間の余地があります。キャッシュをキャッシュに保存しないか、キャッシュに保存するかの 2 つの極端なオプションを紹介しました。前述の「わかりやすい」URL など、一定期間キャッシュに保存できるファイルは数多くあります。

このような「わかりやすい」URL とその HTML をキャッシュに保存する場合は、どのような依存関係が含まれているか、どのような依存関係がキャッシュに保存されるか、一定期間の URL のキャッシュが与える影響について考慮するとよいでしょう。次のような画像を含む HTML ページを見てみましょう。

<img src="/images/foo.jpeg" loading="lazy" />

この遅延読み込み画像を削除または変更してサイトを更新または変更した場合、HTML のキャッシュ バージョンを表示しているユーザーには、正しくない画像が表示されたり、画像が表示されなかったりすることがあります。これは、ユーザーがサイトに再アクセスしたときに、元の /images/foo.jpeg がキャッシュに残っているためです。

注意深く使用すれば、影響を受けない可能性があります。ただし、エンドユーザーがキャッシュに保存すると、サイトはサーバーにのみ存在しなくなることを覚えておくことが重要です。むしろ、エンドユーザーのブラウザのキャッシュ内に断片として存在する可能性があります。

一般に、キャッシュに関するほとんどのガイドでは、1 時間、数時間など、キャッシュに保存する時間について説明しています。この種のキャッシュを設定するには、次のようなヘッダーを使用します(3,600 秒間(1 時間)キャッシュに保存します)。

Cache-Control: max-age=3600,immutable,public

最後にもう 1 つ。ニュース記事など、ユーザーが通常 1 回しかアクセスしないタイムリーなコンテンツを作成する場合は、キャッシュに保存しないことをおすすめします。上記の適切なデフォルト設定を使用してください。ニュース記事や最新の出来事に関する重要な更新など、最新の優れたコンテンツを常に表示したいというユーザーの希望よりも、キャッシュの価値を過大評価していることが多いと思います。

HTML 以外のオプション

HTML 以外にも、中間的なファイルには次のようなオプションがあります。

  • 通常は、他のアセットに影響を与えないアセットを探します。

    • たとえば、CSS は HTML のレンダリング方法を変更するため、使用しないでください。
  • タイムリーな記事の一部として使用される大きな画像

    • ユーザーが 1 つの記事にアクセスする回数は数回程度であるため、写真やヒーロー画像を永久にキャッシュに保存してストレージを浪費しないでください。
  • 自身に存続期間があるものを表すアセット

    • 天気に関する JSON データは 1 時間ごとにしかパブリッシュされないため、前の結果を 1 時間キャッシュに保存すれば、ウィンドウ内で変更されることはありません。
    • オープンソース プロジェクトのビルドはレート制限されている可能性があるため、ステータスが変更される可能性があるまでビルド ステータス イメージをキャッシュに保存します。

概要

ユーザーがサイトを 2 回目に読み込むとき、ユーザーはすでに信頼を表明しています。つまり、ユーザーはサイトに戻って、提供されている商品やサービスをさらに利用したいと考えています。この時点では、常に読み込み時間を短縮できるわけではありません。また、高速かつ最新のエクスペリエンスの提供に必要な作業のみをブラウザが行うように、さまざまなオプションを利用できます。

キャッシュ保存はウェブにおいて新しいコンセプトではありませんが、おそらく妥当なデフォルトが必要です。必要に応じて、キャッシュ戦略を改善するためにオプトインすることを検討してください。今後ともよろしくお願いいたします。

関連情報

HTTP キャッシュに関する一般的なガイドについては、HTTP キャッシュで不要なネットワーク リクエストを防ぐをご覧ください。