CDN とキャッシュの違いとは
このブログ記事は、Rogier Mulhuijzen が「O’Reilly Radar」に寄稿した記事を編集したものです。undefinedundefinedundefined
CDN は本質的にキャッシュ機能であるため、ブラウザキャッシュを使用しないことで複雑化を避けたいと考えているお客様もいらっしゃるかもしれません。しかし、どちらのキャッシュにも、それぞれ独自のメリットがあります。このブログ記事では、各キャッシュのメリットをご紹介し、これらのキャッシュを組み合わせてWebサイトのパフォーマンスを最適化し、優れたユーザーエクスペリエンスを実現する方法をご説明します。
キャッシュバスティングとは
キャッシュバスティングとは、新しいファイルをアップロードしてキャッシュ済みの既存ファイルと置き換えるプロセスを指します。キャッシュバスティングによって、置き換えられる古いファイルをブラウザがキャッシュから取得するのを回避できます。
CDN キャッシュを使用する理由
CDN を使用すると、非常に迅速にアセットを配信できますが、僻地など携帯電話の電波が届きにくい場所にいるユーザーにとってはあまり効果がありません。Cedexis のレポートによると、実のところ米国では、すべての CDN への往復時間 (RTT) の95パーセンタイルが200ミリ秒を大きく上回っています。つまり、少なくともユーザーの5%が、Webサイトまたはアプリケーションの遅延を経験しているということです。参考までに RTT の50パーセンタイルまたは中央値は、約45ミリ秒です。
ではなぜ、ブラウザキャッシュのみに頼らず、わざわざ CDN を使用する必要があるのでしょうか。
より優れたコントロール。ほとんどの CDN で、キャッシュからアセットをパージできます。これはアセットに変更を加える際に非常に便利な機能ですが、ブラウザキャッシュにはこのオプションはありません。
重要な第一印象をアッ プ。ブラウザキャッシュは、ユーザーが初めてサイトを訪問する際にはコールドキャッシュ (利用できるオブジェクトがない状態) であるため、まったく役に立ちません。CDN でコールドブラウザキャッシュによるユーザーエクスペリエンスを可能な限り高速化し、ウォームブラウザキャッシュで連続するページの配信を加速できます。
CDN の地理的なメリット。ユーザーが95パーセンタイルに含まれる場合でも、250ミリ秒の RTT は350ミリ秒の RTT よりまだましです。各アセットをオープン接続で取得するのに少なくとも1往復はするわけですが、その接続を開始するには追加の往復が必要になります。しかもトランスポートレイヤーセキュリティ (TLS、以前の名称は SSL) では接続ごとに少なくともさらに1回、ときには2回の往復が追加されます。こうした追加の往復により、あっという間に RTT が加算されます。
すべてのユーザー間でキャッシュを共有できる CDN の優れた効率性。CDN を利用することで、オリジンサーバーの負荷を低く抑えることが可能になり、プラットフォームに独自のキャッシュレイヤーを設置する必要がなくなります。
CDN とブラウザキャッシュの両方を使用する方法
CDN の重要性とブラウザキャッシュの価値を理解したところで、次はこの2つを組み合わせる手法を2つご紹介します。
ブラウザキャッシュでは有効期限 (TTL) を短く設定し、CDN 側では長い TTL とパージ機能使用する。
CDN とブラウザキャッシュの両方で TTL を長く設定し、バージョンベースのキャッシュバスターを使用する。
以下にそれぞれの手法について詳しく解説し、2つの手法を組み合わせて使用する方法についてもご紹介します。
短い TTL と長い TTL
ブラウザキャッシュでは 、TTL がアクセス時間全体のみをカバーし、それ以上はキャッシュが持続されないことが理想的です。そうすれば、ユーザーの訪問中はページが迅速に読み込まれ、同じユーザーが後で同じサイトに再度アクセスしても過去のアセットが表示されることはありません。分析ツールでサイトの滞在時間を確認できますが、通常5-10分といったところです。
Fastly 側の TTL は1か月または1年程度にし、デプロイパイプラインの一部としてパージをセットアップして、できる限り早く新しいバージョンを配信できるようにすることをお勧めします。パージについて詳しくは、https://docs.fastly.com/ja/guides/purging/ をご覧ください。
注 : 更新するアセットの新しいバージョンが確実にオリジンから配信されることが確認できるまで、パージコマンドを送信しないでください。Fastly にパージリクエストが送信され、オリジンサーバーへの同期が完了する前に Fastly が即座にファイルを取得したことによって、混乱が生じたケースが過去に確認されています。
ブラウザキャッシュの TTL を設定するには、オリジンのレスポンスに Cache-Control
ヘッダーを使用します。さらに、Surrogate-Control
ヘッダーまたは Fastly 設定でオーバーライドを使用して、TTL を設定します。
以下は Surrogate-Control ヘッダーを使用した例です。
Cache-Control: max-age=600
Surrogate-Control: max-age=31536000
上記の例では、Cache-Control ヘッダーはブラウザに10分キャッシュするよう指示し、Fastly には1年間キャッシュするよう指示しています。Surrogate-Control
ヘッダーは、レスポンスがクライアントに送信 される前に削除されるため、この実装の詳細はユーザーからは見えません。Fastly で TTL をコントロールする際に使用できるヘッダーについて詳しくは、Fastly のドキュメントをご覧ください。
バージョンベースのキャッシュバスター
その名前に反して、キャッシュバスターは賢く使用するとキャッシュを改善できます。簡単に言うと、キャッシュバスターは URL に追加される新しいクエリ文字列パラメーターです。Web サーバーと多くのアプリケーションサーバーは関係のないクエリ文字列パラメーターを無視しますが、キャッシュはクエリ文字列のあらゆる差異が結果に影響を与えると仮定します。これはブラウザキャッシュの場合も同じです。以下の3つのオブジェクトに対してサーバーがまったく同じレスポンスを返したとしても、ブラウザはそれぞれ一意のオブジェクトとして扱う必要があります。
https://www.example.com/css/main.css、https://www.example.com/css/main.css?cb=foo
、https://www.example.com/css/main.css?foo=bar
ビルド番号が133のアプリを運用しているとします。アプリは https://www.example.com/css/main.css
にリンクする代わりに、https://www.example.com/css/main.css?v=133
にリンクします。ビルド134を構築する場合は、代わりに https://www.example.com/css/main.css?v=134
にリンクします。ブラウザは、有効期限内のコピーがキャッシュにあっても、この main.css の新しいバージョンを取得する必要があります。
一般的には、ファイルコンテンツの (短い) ハッシュ、ビルド番号、またはコミットハッシュのいずれかを使用します。
個人的にはファイルコンテンツのハッシュを使用することをお勧めします。この方法では、コンテンツが変化した場合にのみキャッシュバスターが変化します。一方、ビルド番号またはコミットハッシュを使用すると、何らかの変更があるたびに必ずキャッシュバスターも変化します。ただし、サイトのアセットのすべての URL にキャッシュバスターを付与する必要があるため、ビルド番号またはコミットハッシュを使用する方が便利な場合もあります。
このアプローチのデメリットは、変更のたびにすべてのアセット URL を更新する必要があるということです。メリットは、長い TTL を使用してブラウザでアセットをキャッシュでき、キャッシュのコンテンツが古くなるとブラウザが新しいアセットを取得できるという点です。
組み合わせて使用する
Fastly の CDN を使用すると、150ミリ秒以内に古いコンテンツをパージできるため、すべての HTML ページをキャッシュすることも検討すべきでしょう。ただし、画像やスタイルシート、JavaScript などのアセットにキャッシュバスターを使用するようサイトを再エンジニアリングしたとしても、実際のページの URL にキャッシュバスターを使用するべきではありません。Google などの検索エンジンでは、ページの URL にクエリ文字列があるとペナルティが課されるためです。
ページをキャッシュする場合は、ページに短い TTL と長い TTL を設定するテクニックを使い、ページで使用されるアセットにキャッシュバスターを使用することを検討してください。
高度な技 : 再検証
ブラウザキャッシュを使用することの二次的なメリットは、オブジェクトの有効期限を過ぎてもブラウザがオブジェクトを保持し、リクエストと一緒に再検証を行う追加のヘッダーを送信することです。リクエストされているオブジェクトが変更されていない場合、CDN は本文なしの 304 Not Modified
ステータスを返し、有効期限を過ぎたオブジェクトを使用できることをブラウザに伝え、任意で新しい TTL を提供できます。
304レスポンスを受け取るリクエ ストの場合でも、処理が完了するまでに必ず往復する必要があることは変わりませんが、レスポンスの本文がないためデータ転送量を大幅に削減できます。これは、低速回線を使用しているユーザーにとってメリットがあるだけでなく、月々の CDN 料金を削減できる可能性もあります。
再検証を有効にするために必要なのは、オリジンからのレスポンスに Last-Modified
ヘッダーまたは ETag
ヘッダーが含まれるようにすることだけです。幸い、ほとんどの Web サーバーではすでに、ディスクから配信する静的ファイルすべてに Last-Modified
ヘッダーおよび ETag
ヘッダーが含まれています。Last-Modified
ヘッダーの値は、ファイルの変更日時に基づきます。ETag
ヘッダーの値は、(Apache の) 変更日時、inode 番号、サイズに基づきます。
ブラウザのキャッシュにある有効期限が切れたオブジェクトに、これら2つのヘッダーのいずれか、あるいは両方が存在することをブラウザが認識すると、Last-Modified
の値を持つ If-Modified-Since
ヘッダーと、ETag
の値を持つ If-None-Match
ヘッダーが追加されます。If-None-Match
と ETag
の値が同じ場合、および If-Modified-Since
の値が Last-Modified
の値と一致するか、それより後である場合、オブジェクトに変更がないとみなされます。
静的アセット向けに単一の Web サーバーを使用している場合、共通のデフォルト設定によって、すでに再検証が完全に機能していると考えられます。
しかし、冗長性を確保するために複数の Web サーバーを使用し、各サーバーが共有ストレージではなくそれぞれローカルストレージを使用している場合、ランダムに再検証の失敗が発生する場合があります。なぜなら、特定のファイルが各 Web サーバーで同じ inode 番号を割り当てられるとは限らず、生成される ETag ヘッダーはサーバーによって異なるからです。また、ほとんどのデプロイスクリプトはファイルをコピーする際に変更日時を保持しないので、Last-Modified
ヘッダーの値も異なります。
これは2つの理由で好ましくありません。まず、Fastly がオリジンと通信する場合、304レスポンスの代わりに本文を含む完全なレスポンスが送信され、無駄に帯域が使用される可能性があります。次に、Fastly は異なるサーバーで異なる ETag
と Last-Modified
の値を保持することになる可能性があります。ブラウザがリクエストのたびに同じサーバーと通信するという保証はないため、値の不一致が原因で必要のない完全なレスポンスが返される可能性があります。
ローカルストレージを使用する Web サーバーが複数ある場合、再検証を最大限に活用するには ETag
をオフにして Last-Modified
のみを使用し、ファイルをコピーする際にデプロイスクリプトで変更日時を保持することをお勧めします。
Google Cloud Storage や Amazon S3 などのクラウドストレージプロバイダーを使用している場合、ETag
ヘッダーはすでにコンテンツのハッシュに自動的に設定されているはずです。従って、同一のファイルを再アップロードしても ETag
は変化しないため、クラウドストレージは Fastly にとって最適なオリジンのひとつです。
さらに詳しく知りたい場合
CDN とブラウザキャッシュを最大限に活用するため、このブログ記事がお役に立てば幸いです。パフォーマンス測定の微調整 (および CDN パフォーマンスの最適化) について詳しくは、Fastly の VP of Technology、Hooman Beheshti のブログ記事「キャッシュヒット率の真実」と「エッジにおけるキャッシュヒット率」をご覧ください。