キャッシュを大切に ❤️

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

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

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

この投稿では、実用的な最新のキャッシュ デフォルトについて説明します。実際にキャッシュをまったく実行しないデフォルトについて説明します。これは単なるデフォルトでありご一読ください。

目標

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

  1. ユーザーが利用可能な最新バージョンを確実に入手できるようにしましょう。変更を行った場合は、すぐに反映させる必要があります。
  2. #1 を実行します。その際、ネットワークから取得するデータをできるだけ少なくします。

大まかに言えば、クライアントがサイトを再度読み込むときに、小さな変更のみを送信するのが理想的です。また、あらゆる変更が最も効率的に配信されるようにサイトを構成することは困難です(詳細は以下の動画と動画をご覧ください)。

とはいえ、キャッシュについては別の要素もあります。たとえば、ユーザーのブラウザの HTTP キャッシュをサイトに長期間保持して、ネットワーク リクエストによるキャッシュ処理をまったく必要としない場合もあるでしょう。または、サイトが最新かどうかを確認する前に、完全にオフラインで処理する Service Worker を作成することもできます。これは極端なオプションであり、多くのオフラインファーストのアプリのようなウェブ エクスペリエンスで有効ですが、ウェブがキャッシュのみの極端なものや、完全にネットワーク限定の極端なものである必要はありません。

背景

ウェブ デベロッパーとして、「古いキャッシュ」の概念をよく理解しています。「ハード更新」を行う、シークレット ウィンドウを開く、ブラウザのデベロッパー ツールを使用してサイトのデータを消去する、といった方法があります。

インターネットの常連のユーザーはそれほど贅沢なことではありません。Google には、ユーザーに 2 回目の読み込みを快適に行うという重要な目標がありますが、ユーザーに不便な時間や行き詰まらないようにすることも非常に重要です。(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 レスポンスを受け取りますが、ネットワークにアクセスして確認する必要があるため、レイテンシがかかります。これがこのアプローチの主なデメリットです。これは、第 1 の世界で高速接続を利用し、選択した 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

この値は年(秒単位)です。仕様によれば これは実質的に 「forever」と同じです

重要な点は、これらのハッシュを手動で生成しないことです。手作業が多すぎることです。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" />

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

注意すれば、影響を受けないかもしれません。しかし大まかに言えば、エンドユーザーがキャッシュしているサイトは、もはやサーバー上に存在するだけでは不十分です。エンドユーザーのブラウザのキャッシュに断片化されている場合があります。

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

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

最後にもう 1 つ、ニュース記事のように、通常はユーザーが 1 回しかアクセスしないタイムリーなコンテンツを作成している場合、私の意見では、コンテンツはキャッシュに保存してはいけないと考えています。上記の適切なデフォルト値を使用する必要があります。私たちは、ニュースや時事の重要な最新情報など、常に最新の優れたコンテンツを見るというユーザーの要望よりも、キャッシュの価値を過大評価してしまうことがよくあります。

HTML 以外のオプション

HTML のほかに、中間にあるファイルには次のようなオプションもあります。

  • 一般的に、他のアセットに影響を与えないアセットを探す

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

    • ユーザーが 1 つの記事にアクセスする回数はおそらく数回程度なので、写真やヒーロー画像を永久にキャッシュすることや、ストレージの無駄使いは避けてください。
  • ライフタイムがあるものを表すアセット

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

まとめ

ユーザーがサイトを再度読み込むときは、すでに信頼度が高まっています。ユーザーは、サイトに戻ってきて、提供しているサービスをもっと利用したいと思っています。現時点では、単に読み込み時間を短縮するだけでは不十分です。高速かつ最新のエクスペリエンスの提供に必要な処理のみをブラウザが実行できるようにするオプションが多数用意されています。

キャッシュはウェブの新しい概念ではありませんが、妥当なデフォルトが必要になるかもしれません。必要に応じて、いずれかのオプションを使用し、より優れたキャッシュ戦略を有効にすることを強く検討してください。今後ともよろしくお願いいたします。

関連情報

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