キャッシング
序章
この20年間で、私たちがウェブアプリケーションを体験する方法は変化し、よりリッチでインタラクティブなコンテンツが提供されるようになりました。しかし、残念ながら、このようなコンテンツには、データストレージと帯域幅の両方においてコストがかかっています。ほとんどの場合、使用するネットワークが劣化していたり、デバイスに十分な容量がなかったりすると、多くの人がウェブ製品を完全に体験することが難しくなります。キャッシュは、このような問題の解決策であると同時に原因でもあります。数多くの選択肢を使いこなすことで、ハイエンドデバイスだけでなく、ローエンドデバイスから製品にアクセスする次の10億人のユーザーにも対応した構築が可能になります。
キャッシュは、JavaScript、CSSファイル、基本的な文字列値などの単純な静的アセットから、より複雑なJSON APIレスポンスまで、以前にダウンロードしたコンテンツの再利用を可能にする技術です。
その核心は、キャッシュによって特定のHTTPリクエストを回避し、アプリケーションがユーザーに対してより応答的で信頼性が高いと感じられるようにすることです。各リクエストは通常、主に2つの場所にキャッシュされます。
- コンテンツデリバリーネットワーク (CDNs) は通常、サードパーティの会社で、ユーザーがアプリケーションにアクセスしている場所にできるだけ近い場所でデータを複製することを主な目的としています。ほとんどのCDNは、いくつかのデフォルトの動作を持っていますが、主にヘッダーを使用することによって、キャッシュの方法を指示できます。
- ブラウザは、体験を最適化するためにあなたが定義したHTTPヘッダーを尊重するか、またはいくつかの内部デフォルトを適用します。その上、ブラウザは、単純な文字列をcookiesに、複雑なAPI応答をIndexedDBに、あるいはリソース全体を サービスワーカー で キャッシュストレージ に保存するなどの手動キャッシュ戦略を利用することも可能です。
この章では、ブラウザとCDNの間で使用されるHTTPヘッダーに主に焦点を当て、サービスワーカーのキャッシュ戦略についても簡単に触れます。
CDNキャッシュの採用
コンテンツデリバリーネットワーク(CDN)は、通常、データのコピーを保存する複数の場所に分散したサーバーのグループです。これにより、サーバーはエンドユーザーへもっとも近いサーバーに基づいてリクエストを処理できます。
2021年のウェブ全体では、Desktopに使用されているCDNはCloudflareがもっとも普及しており、総ページ数の14%、次いでGoogleが6%となっています。CloudflareとGoogleがもっとも普及していますが、この2つ以外にも、Fastly、Amazon CloudFront、Akamaiなど、多種多様なソリューションが残っています。
サービス・ワーカー採用
サービスワーカーの採用が着実に増え続けています。
サービスワーカーを登録しているページは1%強であるのに対し、アクセス数の多い上位1,000ドメインにランクインしているページの約9%がサービスワーカーを登録しています。
このように、とくに上位1,000ページにおいてサービスワーカーの採用率が高いのは、リモートファースト、そしてそれに関連してモバイルフレンドリーという世界的なトレンドと関係があるのかもしれません。1年を通じて1つの場所で仕事をしたり生活したりすることへの依存度がシフトするにつれて、私たちはデバイスが私たちについていくために、さらにハードでスマートな働きをすることを必要としているのです。サービスワーカーは、ユーザーが信頼性の低いネットワークやローエンドデバイスを使用している場合、パフォーマンスを向上させることができるツールです。
サービスワーカー内のリソースをキャッシュする主な方法は、キャッシュストレージAPIを使用することです。これにより、開発者はワーカーを通過するすべてのリクエストに対して、カスタムキャッシュ戦略を作成できます。よく知られているものに、stale-while-revalidate、ネットワークにフォールバックするキャッシュ、キャッシュにフォールバックするネットワーク、キャッシュのみがあります。近年では、プラグアンドプレイでキャッシュを決定できるワークボックスの人気が高まっているおかげで、それらの戦略を採用するのがさらに容易になっています。
サービスワーカー、およびWorkboxについては、PWAの章で詳しく説明します。
キャッシュヘッダーの採用
CDNとブラウザの両方において、HTTPヘッダーは開発者がリソースを適切にキャッシュするために習得しなければならない主要なツールです。ヘッダーとは、HTTPリクエストやレスポンスから読み取られる単純な命令であり、それらのいくつかは、使用するキャッシュ戦略を制御するのに役立ちます。
キャッシュ関連ヘッダーは、それがあるかないかで、ブラウザやCDNに3つの重要な情報を伝えます。
- Cacheability: このコンテンツはキャッシュ可能か?
- Freshness: キャッシュ可能な場合、どのくらいの期間キャッシュできるのか?
- Validation: キャッシュ可能な場合、キャッシュされたバージョンがまだ新鮮であることを確認するにはどうすればよいか?
ヘッダーは単独、あるいは一緒に使うものです。コンテンツがキャッシュ可能で新鮮であるかどうかを判断するため。
Expires
は、明示的に有効期限を指定します(つまり、コンテンツの正確な有効期限を指定します)。Cache-Control
はキャッシュ期間 (すなわち、コンテンツが生成されたときから相対的に、ブラウザにキャッシュできる期間)を指定します。
両方が指定された場合、Cache-Control
が優先されます。
2019年以降、Cache-Control
ヘッダーの使用率は着実に増加しています。モバイルリクエストのレスポンスの74.2%が Cache-Control
ヘッダーを含み、デスクトップリクエストのレスポンスの74.8%がこのヘッダーを利用していました。
2020年以降、この特定のヘッダーの使用率は、モバイルで0.71%、デスクトップでは1.13%増加しました。しかし、モバイルではまだ25.1%のリクエストが Cache-Control
と Expires
ヘッダーのどちらも使用していません。このことから、コミュニティでは Cache-Control
の適切な使用に関する認識が高まっていると思われますが、これらのヘッダーを完全に採用するにはまだ長い道のりがあります。
内容を検証するために、私たちは
Last-Modified
は、オブジェクトが最後に変更された日時を示します。この値は日付のタイムスタンプです。ETag
(Entity Tag) は引用符で囲まれた文字列として、コンテンツに一意の識別子を与えます。これは、サーバーが選択した任意の形式を取ることができます。通常はファイルの内容のハッシュ値ですが、タイムスタンプや単純な文字列でもかまいません。
両方が指定された場合、ETag
が優先される。
2020年と2021年を比較すると、ETag
の利用が年々わずかに増え、Last-Modified
の利用が1.5%減るという、過去繰り返された傾向にあることがわかります。来年注目すべきは、ETag
やLast-Modified
ヘッダーを使用しない回答が1.4%増加するという新しい傾向で、これはコミュニティがこれらのヘッダーの価値を理解していないことを示唆している可能性があるのです。
Cache-Control
ディレクティブ
Cache-Control
ヘッダーを使用する場合、特定のキャッシュ機能を示す1つ以上のディレクティブ定義された値を指定します。複数のディレクティブはカンマで区切られ、どのような順番でも設定できますが、中には互いに衝突するものもあります(たとえば、public
とprivate
など)。さらに、いくつかのディレクティブは max-age
のような値をとります。
以下は、もっとも一般的な Cache-Control
ディレクティブを示した表です。
ディレクティブ | 説明 |
---|---|
max-age |
現在時刻を基準として、リソースのキャッシュが可能な秒数を示す。たとえば、max-age=86400 などです。 |
public |
ブラウザや CDN を含む、任意のキャッシュが応答を保存できることを示します。これはデフォルトで想定されています。 |
no-cache |
キャッシュされたリソースは、たとえそれが古いとマークされていなくても、使用前に条件付きリクエストによって再検証されなければならない。 |
must-revalidate |
キャッシュされた古いエントリは、使用前に条件付きリクエストによって再検証されなければならない。 |
no-store |
レスポンスがキャッシュされてはならないことを示す。 |
private |
このレスポンスは特定のユーザーを対象としたものであり、CDNなどの共有キャッシュに保存されるべきものではありません。 |
immutable |
キャッシュされたエントリがその TTL の間、決して変更されず、再バリデーションが必要ないことを示す。 |
max-age
ディレクティブはもっとも一般的で、62.2%のデスクトップ・リクエストがこのディレクティブを含む Cache-Control
レスポンス・ヘッダーを含んでいます。
2020年との比較では、上図の上位7つの指令のほとんどとともに、デスクトップではmax-age
の採用が2%増加しました。
immutable
ディレクティブは比較的新しいもので、特定のタイプのリクエストのキャッシュ性を大幅に向上させることができます。しかし、まだ一部のブラウザでしかサポートされておらず、Wixが16.4%、Facebook 8.6%、Tawk 2.8%、Shopify 2.4%といったホストネットワークから来るリクエストが、ほとんどであることが分かっています。
もっとも誤用されている Cache-Control
ディレクティブは、引き続き set-cookie
で、デスクトップでは ディレクティブ 全体の0.07% で、モバイルでは0.08% で使用されています。しかし、2020年からは0.16%の使用量削減という意味でも喜ばしいことです。
no-cache
、max-age=0
、no-store
が一緒に使われている場合を見てみると年々増加傾向にあり no-store
が no-cache
と max-age=0
のいずれか/両方と一緒に指定されると、 no-store
ディレクティブが優先され、他のディレクティブが、無視されることが分かってきています。これらのディレクティブの使用について、たとえば大規模なカンファレンスの際にもっと認識を高めることで、誤ってムダなバイトを使用することを避けることができるかもしれません。
おもしろい事実:もっとも一般的なmax-age
の値は30日であり、もっとも大きな値は51兆年です。
304
Not Modified ステータス
サイズに関して言えば、304 Not Modified
のレスポンスは 200 OK
のレスポンスよりもずっと小さいので、必要なサイズのデータのみを配信することでページのパフォーマンスを向上させることができるということになります。そこで、条件付きリクエストを正しく使用することが重要になります。再バリデーション、つまりデータの節約は、 ETag
ヘッダーまたは Last-Modified
ヘッダーのどちらかを使用することで行うことができます。
Last-Modified
レスポンスヘッダーは If-Modified-Since
リクエストヘッダーと一緒に動作し、リクエストされたファイルに何らかの変更がなされたかどうかをブラウザに知らせます。
2020年から2021年にかけて、If-Modified-Since
の304レスポンスの分布が、7.7%増加したことがわかりました。これは、コミュニティがこれらのデータ節約を活用していることを示しています。
日付文字列の有効性
タイムスタンプを表すために使用される3つの主要なHTTPヘッダー、 Date
、Last-Modified
、Expires
はすべて日付の書式を持つ文字列を使用します。HTTPレスポンスヘッダーの Date
はほとんどの場合、ウェブサーバによって自動的に生成されるため、無効な値は非常に稀です。しかし、日付が正しく設定されていない場合、それが提供されるレスポンスのキャッシュに影響を与える可能性があります。
2020年から2021年にかけて無効なDate
を使用する割合は0.5%改善しましたが、Last-Modified
とExpires
については悪化しており、キャッシュ時の日付設定に関係が、あることがわかります。
このことから、日付ベースのヘッダーの自動化には、さらなる注意が、必要であることがわかる。
Vary
リソースのキャッシュに不可欠なステップは、そのリソースが以前にキャッシュされていたかどうかを理解することです。ブラウザは通常、キャッシュキーとしてURLを使用します。同時に、同じURLに対するリクエストでも Accept-Encoding
が異なるとレスポンスが異なるため、不正にキャッシュされる可能性があります。そのため、Vary
ヘッダーを使用して、1つ以上のヘッダーの値をキャッシュキーに追加するようにブラウザに指示します。
もっとも普及している Vary
ヘッダーは Accept-Encoding
で90.3% の使用率、次いで User-Agent
で10.9% 、 Origin
で10.1% 、そして Accept
で4.8% の使用率です。
2020年からAccept-Encoding
の使用率が1.5%減少していることがわかりました。
監査した総リクエストのうち、Vary
ヘッダーを使用しているのは46.25%に過ぎませんが、2020年と比較すると、全体で2.85%増加していることを指摘することが重要です。
Vary
ヘッダーを使用するリクエストのうち、83.4%は Cache-control
も使用しています。これは、2020年から2.1%改善されたことを示しています。
キャッシュ可能なレスポンスにCookieを設定する
2020年のキャッシュの章では、キャッシュ可能なレスポンスで set-cookie
を使用することに注意するよう念を押しました。なぜなら、レスポンスのわずか4.9%が private
ディレクティブを使用しており、ユーザーの個人データがCDN経由で誤って別のユーザーに提供される危険性があるためです。
2021年には、set-cookie
とキャッシュの共存に関する認識が高まっていることがわかります。set-cookie
でprivateディレクティブを使用しているウェブページはまだ5%しかありませんが、キャッシュ可能な set-cookie
レスポンスの総数は4.41%減少しています。
どのようなコンテンツをキャッシュしているのか?
フォント、CSS、オーディオファイルは99%以上キャッシュ可能で、現在ほぼ100%のページでフォントがキャッシュされています。これは、静的なファイルであるため、キャッシュに適しているためと思われます。
しかし、もっともよく使われるリソースの中には、動的な性質のためか、キャッシュ不可能なものがあります。とくに、HTMLは23.4% ともっとも高い割合でキャッシュ不可能なリソースがあり、画像は10.1% でそれに続いています。
2020年と2021年のモバイルデータを比較すると、キャッシュ可能なHTMLが5.1%増加していることがわかります。これは、サーバーサイドレンダリングアプリケーションによって生成されたようなHTMLページをキャッシュするために、CDNをより良く利用する方向に進んでいる可能性を物語っています。ページは通常、特定のウェブページのコンテンツが頻繁に変更されない場合、SSRによって生成されます。このURLは、数週間あるいは数ヶ月間、同じHTMLを提供する可能性があり、そのコンテンツは高度にキャッシュ可能です。
タイプ | デスクトップ | モバイル |
---|---|---|
テキスト | 0.2 | 0.2 |
XML | 1 | 1 |
その他 | 1 | 1 |
動画 | 4 | 8 |
HTML | 3 | 14 |
オーディオ | 0.2 | 30 |
CSS | 30 | 30 |
画像 | 30 | 30 |
スクリプト | 30 | 30 |
フォント | 365 | 365 |
すべてのリソースタイプでTime To Live(TTL)の中央値を見てみると全体で同じような割合のキャッシュをしていても、モバイルでは、とくにHTML、オーディオ、ビデオでかなり長いキャッシュが、存在することがわかります。
とはいえ、モバイル体験のための最適化を続けていても、キャッシュ可能なデスクトップリソースの潜在的な量は、モバイル用のリソースよりわずかに多いままであることは興味深いことです。
キャッシュTTLとリソースエイジの比較は?
画像と動画は、ファースト・ソースでも、サード・パーティ・ソースでも、同じ平均年齢を維持していることがわかる。画像は一貫してリソース年齢が2年であるのに対し、動画のリソースはほとんどが8~52週であった。
他のコンテンツタイプに分けると、サードパーティーのフォントは、8~52週間の間にもっとも多くキャッシュされ、72.4%であることがわかりました。しかし、ファーストパーティーの場合、リソースの年齢層は8~52週と2年以上に均等に分かれており、かなり大きなばらつきがあります。オーディオとスクリプトについても同様の結果が出ており、ファーストパーティでは8~52週、サードパーティでは1~7週が大半を占めています。
オーディオは、ファーストパーティーとサードパーティーの両方で、もっとも高度にキャッシュされたリソースでした。しかし、リソース年齢は、ファーストパーティ(平均8~52週間)とサードパーティ(わずか1~7週間)で大きく異なっていました。ファーストパーティーのオーディオリソースは更新頻度が、低い傾向があるため(なぜ?)、サードパーティーは、より新鮮なリソースを提供することでキャッシュの機会を利用している可能性があります。
キャッシュされたファーストパーティーのCSSの最大グループ(32.2%)は8~52週間の傾向があり、サードパーティーの最大グループは1週間未満で、その期間にキャッシュされたリソースは51.8%でした。
もっとも、HTMLは1週間未満で42.7%、サードパーティは1~7週間で43.1%と、ファーストパーティのグループがもっとも多くサービスを提供しています。
このデータを検討した上での考察。
- ファーストパーティではHTML、サードパーティではCSSがもっとも新鮮なコンテンツです。
- ファースト、サードパーティともに、もっとも陳腐化したコンテンツは画像です。
このデータからファーストパーティは、JSやCSSファイルへのリンクを含むHTMLコンテンツの更新を優先し、ブラウザ拡張機能のようにCSSやスクリプトを主体とするサードパーティは、CSSを最新の状態に保つことを優先していることがわかります。ファーストパーティとサードパーティの違いを考えると、サードパーティにとってコンテンツの配信方法は実際のコンテンツよりも重要であり、そのためコンテンツの表示と最適化が、より重要であることが分かります。
コンテンツ年齢と比較してキャッシュTTLが短すぎるとされたモバイルリソースは、2020年以降に改善が見られました。このデータは、コミュニティが適切な相対キャッシュについて理解を深めていることを示唆するものであり、エキサイティングなものです。
キャッシュTTLが長すぎると、古くなったコンテンツが、提供される可能性がありますが、短すぎるとエンドユーザーにとって何のメリットもありません。キャッシュTTLとコンテンツエイジの関係は、2020年の60.2%から2021年には54.3%となり、この差は徐々に縮まってきています。コンテンツエイジ(ページのHTMLやCSSなどの改修頻度)に対する気配りができればできるほど、より正確にキャッシュの上限を設定することができるようになるのです。
開発者はキャッシュ期間をコンテンツ年齢により正確に設定できるようになってきており、その結果、より責任ある、つまりより効果的なキャッシュを実現できるようになっています。
クライアント | ファーストパーティ | サードパーティ | 全体 |
---|---|---|---|
デスクトップ | 59.5% | 46.2% | 54.3% |
モバイル | 60.1% | 44.7% | 54.3% |
ファーストパーティプロバイダーとサードパーティプロバイダーに分けると、もっとも改善されたのはサードパーティで、13.2%の改善が見られました。世界中の企業が、開発者向けにキャッシュを最適化する製品を開発していることは、非常に心強いことです。開発者コミュニティのパフォーマンス向上に対する関心の高まりが、サードパーティによるキャッシュ戦略の最適化を促し、さらにはその動機付けになった可能性があります。
しかし、今後数年間、ファーストパーティがどのように効果的な改善をしていくかという課題は残されています。
キャッシュの機会を特定する
LighthouseのキャッシングTTLスコアに基づき、100点満点でランクインしたページが2020年の3.3%から2021年には4.4%に増加するという改善が見られました。
このスコアは、ページがキャッシュポリシーの追加的な改善によって恩恵を受けることができるかどうかを反映しています。31% のページが50パーセンタイルのスコアを上回ったことは喜ばしいことですが、25パーセンタイル以下の52% のページには大きな改善の可能性があります。
このことから、Webページにはある程度のキャッシュ機能が備わっていても、そのポリシーの使い方が古く、自社製品の最新状態に最適化されていないと考えざるを得ません。
2020年から2021年にかけてのLighthouseのwasted bytes監査に基づくと、繰り返し閲覧される監査対象の全ページにおいて、ムダなバイトが3.28%改善されました。これは、1MBをムダにするページの割合を42.8%から39.5%に下げ、有料のインターネットデータプランを持つ海外のユーザーにとって、よりコストの低い製品を作るというコミュニティのかなりの傾向を示しています。
現在、監査されたページのうち、ムダなバイトが0である割合は1.34%とまだ比較的低いです。今後数年間は、コミュニティがウェブパフォーマンスの最適化に注力し続けるため、この割合が増加することを期待しています。
結論
故・偉大なPhil Karltonは、「コンピューター サイエンスには難しいことが2つしかない」という有名な言葉を残しました。正直なところ、私はキャッシュがなぜそんなに難しいのか、いつも不思議に思っています。私の考えでは、キャッシュをうまく行うには、2つの重要な要素が必要です:シンプルに保つこと、そして潜在的なエッジケースをすべて理解することです。
残念ながらキャッシュを賢くしようとしすぎると、間違ったものをキャッシュしてしまったり、もっと悪いことに、過剰にキャッシュしてしまったりすることがあるのです。同じようなことですが、すべてのエッジケースを理解するには、広範な調査とテスト、そしてゆっくりとした漸進的な改良が必要です。それでも、古いブラウザがあなたをバスの下に投げ出さないことを願わなければなりません。しかし私たちがいまだに優れたキャッシュ戦略を追い求める理由は、ラウンドトリップリクエストの大幅な削減、サーバーの高い節約、ユーザーから求められるデータの削減、そして最終的にはより良いユーザー体験という、究極の報酬が非常に大きいからです。
どのような場合でも、キャッシュの最適な使用方法のプレイブックを用意するようにしてください。
- 開発サイクルの初期段階、および製品出荷後のキャッシュ作業を優先させる。
- 主要なエッジケースを再現するエンドツーエンドのテストを書く
- 定期的なサイト監査と、古くなったり欠落している可能性のあるキャッシュルールの更新
最終的には私たちが仲間を指導し、理解しやすい良いドキュメントを書くことによって知識を広めることができればキャッシングは、それほど複雑なものではなくなります。キャッシングは、一部の人だけがマスターすればいいというものではありません。私たちの目標は、会社全体の共通認識として定着させることです。なぜなら、最終的に私たちが本当に注力したいのは、ユーザーにとって簡単で摩擦のない体験を構築することだからです。