💭

Vercelのキャッシュを完全に理解しました (?)

2023/05/12に公開

はじめに

先日(2023/5/1)にVercelの新機能・サービスを発表するビッグイベント 『 Vercel Ship 』 が開催されました。

Next.jsやVercelの激アツな発表がたくさんあり、非常に大きな盛り上がりを見せているように感じます。

今回は、最近社内でも活用されているISRという技術を支えるstale-while-revalidateに触れつつ、Vercel(with Next.js)のキャッシュについて、最低限知っておきたい知見について共有できたらなと思います。

Vercelのインフラストラクチャ概要

Vercel Edge Network を使用すると、顧客の近くにコンテンツを保存し、データに近いリージョンでコンピューティングを実行できるため、待ち時間が短縮され、エンドユーザーのパフォーマンスが向上します。

エッジ ネットワークは、コンテンツ配信ネットワーク (CDN) であると同時に、エッジでコンピューティングを実行するためのグローバル分散プラットフォームでもあります。

引用: https://vercel.com/docs/concepts/edge-network/overview

VercelのインフラストラクチャはVercel Edge Networkと言い、分散型のエッジコンピューティングでアプリケーションをホスティングすることができます。

Vercel Caching

Vercelのキャッシュ機能は、デプロイメントやドメインに対して自動的に適用されます。

またすべてのプランで利用可能であり、静的コンテンツのキャッシュは自動的に行われますが、Edge FunctionsやServerless Functionsを通じて動的コンテンツをキャッシュする場合は、Cache-Controlヘッダーをレスポンスに追加する必要があります。

Vercelキャッシュの特徴の一つにstale-while-revalidateというCache-Controlヘッダのディレクティブのサポートが挙げられます。

stale-while-revalidateとは

そもそもstale-while-revalidateとは、Cache-Controlヘッダのディレクティブの一種です。
stale-while-revalidateディレクティブの最大の利点は、やはり古いキャッシュの再利用ができる点です。

stale-while-revalidateは「キャッシュは切らさずに持っておきたいが、できるだけ最新のデータを返すようにしたい」みたいなユースケースで非常に効果を発揮します。

max-age with stale-while-revalidate vs only max-age

max-ageのみを使用した場合と比較しながら、stale-while-revalidateの挙動について確認していきたいと思います。

【 only max-age 】

まずは、従来のmax-ageのみでキャッシュの設定を行なった場合の挙動を見ていきます。

max-ageのみの場合は、キャッシュの有効期限はシンプルにmax-age=NのN秒になります。
N秒経過後の最初のリクエストは、下記の画像と同様に新たなリソースのfetchが走ります。
そのfetchのレスポンスに応じてまた新たにキャッシュが設定されます。

【 max-age with stale-while-revalidate 】

次に、max-ageとstale-while-revalidateを組み合わせたパターンの挙動を確認していこうと思います。

初回のリソースfetch後、max-ageの有効期限の間は、全てのリクエストに対してキャッシュを返します。
次にmax-age経過後の最初のリクエストに対しては、古いキャッシュ(stale)が返されます。
その際にバックグラウンドで非同期的にサーバに対してキャッシュの再検証を行います。
この再検証を行なっている間は、この古いキャッシュ(stale)は再利用可能になります。

以上がstale-while-revalidateを活用した際のキャッシュの挙動になります。

要するに、「データが更新されたら、キャッシュも更新しておくよ」をよしなにやってくれるといったイメージかなと思います。

キャッシュ可能なレスポンスの基準と制御方法

Vercel Edge Networkでレスポンスがキャッシュされるためには、以下の条件を満たす必要があります。

  1. リクエストはGETまたはHEADメソッドを使用する
  2. リクエストにRangeヘッダーが含まれていない
  3. リクエストにAuthorizationヘッダーが含まれていない
  4. リクエストに_vercel_no_cache=1のクッキーが含まれていない
  5. リクエストに?_vercel_no_cache=1のリクエストパラメータが含まれていない
  6. レスポンスが200、404、301、または308のステータスコードを使用する
  7. レスポンスのコンテンツ長が10MB以下である
  8. レスポンスにset-cookieヘッダーが含まれていない
  9. Cache-Controlヘッダーにprivateno-cache、またはno-storeディレクティブが含まれていない

キャッシュの設定方法

静的ファイルのキャッシュ

静的ファイルは、最初のリクエスト後に自動的にエッジでキャッシュされ、Vercel Edge Networkで最大31日間キャッシュされます。
静的ファイルはハッシュによってキャッシュされるため、静的ファイルが変更されない場合はデプロイをまたいで持続することが可能です。

サーバーレス関数とエッジ関数のキャッシュ

サーバーレス関数やエッジ関数を使用した場合にレスポンスをキャッシュするには、Cache-Controlヘッダーに以下のディレクティブを含める必要があります。

  • サーバー
    s-maxage=N or s-maxage=N, stale-while-revalidate=N
  • ブラウザ
    max-age=N, public or max-age=N, immutable

サーバーサイドのキャッシュヘッダーは、Edgeでレスポンスをキャッシュするために使用されます。Vercel Edge Networkは、レスポンスをブラウザに送信する前に、s-maxageとstale-while-revalidateヘッダーをレスポンスから取り除きます。 キャッシュからレスポンスが提供されたかどうかを判断するには、レスポンスのx-vercel-cacheヘッダーを確認します。

公式が推奨するキャッシュオプションの設定

We recommend that you set your cache tomax-age=0, s-maxage=86400, adjusting 86400 to the number of seconds you want the response cached. This configuration tells browsers not to cache, allowing the Vercel Edge Network to cache responses and invalidate them when deployments update.

公式曰く、キャッシュの設定には、max-age=0, s-maxage=86400とし、86400をキャッシュしたい秒数に調整することをお勧めされています。
この設定では、ブラウザはキャッシュせず、Vercel Edge Networkがレスポンスをキャッシュし、デプロイメントが更新されたときに無効になるそうです。

X-Vercel-Cacheヘッダー

レスポンスには、リソースのキャッシュ状態に関する情報を提供するX-Vercel-Cacheヘッダーが含まれています。このヘッダーの値は、HIT、MISS、STALE、PRERENDER、REVALIDATEDのいずれかになります。
このx-vercel-cacheヘッダーにより、キャッシュの状態の詳細を確認できます。

Head Head
HIT リソースがキャッシュから提供されたことを示します。
MISS リソースがキャッシュに存在しなかったため、オリジンサーバーから取得されたことを示します。
STALE staleなキャッシュが提供されたことを示します。
PRERENDER Next.jsででSSGした(getStaticProps + fallback: true)時、オリジンサーバからリソースが提供されたことを示します。(Edge Cacheでキャッシュは見つからない)
REVALIDATED キャッシュの再検証が完了し、キャッシュが更新された状態を指します。

キャッシュの無効化

Vercelでは、デプロイメントごとに一意のキーがキャッシュに使用され、ユーザーが前のデプロイメントのコンテンツを表示しないようにします。キャッシュは新しいデプロイメントが作成されると自動的に消去されますが、必要に応じて再デプロイでVercel Edge Networkのキャッシュを無効化することができます。

まとめ

ニュースメディアなどの「定期的にコンテンツの更新がある」ような開発でVercelを利用する場合は、stale-while-revalidateを活用したキャッシュ戦略がかなり肝になってくるのかなと思います。

ぜひ参考にしてみてください。

参考情報

https://vercel.com/docs/concepts/edge-network/caching
https://vercel.com/docs/concepts/incremental-static-regeneration/overview
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
https://blog.jxck.io/entries/2016-04-16/stale-while-revalidate.html


Discussion