by MintJams

Cache-Controlの新しい波:stale-while-revalidateでWebを高速化&堅牢化

stale-while-revalidate(SWR)とstale-if-error(SIE)は、Webパフォーマンスと可用性を劇的に向上させる強力なHTTPキャッシュ戦略です。本記事では、その仕組みからNGINX設定、CDNでの活用方法までを徹底解説します。

HTTP キャッシュ戦略は、Web パフォーマンス改善の鍵です。従来の max-age ディレクティブだけでは、キャッシュの有効期限切れ時にユーザーを待たせてしまうという課題がありました。本記事では、この問題を解決し、Web パフォーマンスを劇的に向上させる stale-while-revalidate (SWR) と stale-if-error (SIE) という2つの強力なレスポンスヘッダー設定について解説します。


SWR と SIE の基本概念と違い

SWR と SIE は、どちらも古い(stale)キャッシュを有効活用するための技術ですが、その目的と使いどころが異なります。

項目 stale-while-revalidate (SWR) stale-if-error (SIE)
古いデータ利用 max-age 切れ後も即表示 max-age 切れ後、エラー時のみ表示
主な目的 高速表示と自動更新 障害時の耐障害性
更新タイミング バックグラウンドで再検証 正常取得できるまで保持
想定ユースケース ダッシュボード、ニュースサイト オフライン対応、API 障害対策

stale-while-revalidate(SWR)

SWR は「古い(stale)データを表示しながら、バックグラウンドで再検証(revalidate)する」キャッシュ戦略です。

  1. 高速な初期表示: ユーザーは古いキャッシュを即座に見られるため、待機時間がありません。
  2. 常に最新のデータ: 裏側で最新データを取得し、次回アクセス時に更新されます。

Cache-Control ヘッダー例

Cache-Control: max-age=60, stale-while-revalidate=3600

この設定により、60秒間は最新データとして扱い、その後 3600秒間は古いデータを表示しつつ、バックグラウンドで再検証が行われます。


stale-if-error(SIE)

SIE は「エラーが発生した場合に限り、古い(stale)データを表示する」キャッシュ戦略です。

  1. サービスの可用性維持: オリジンサーバーが応答しなかったり、ネットワークエラーが発生したりした場合でも、キャッシュされたデータが表示され、ユーザーは白画面を見ることがなくなります。

Cache-Control ヘッダー例

Cache-Control: max-age=60, stale-if-error=86400

この設定では、max-age が切れた後、オリジンサーバーでエラーが発生した場合に限り、86400秒(1日)の間、古いキャッシュを使い続けます。


実装パターン

1. オリジンサーバーでのヘッダー設定

NGINX の location ブロック内で add_header ディレクティブを使用します。

location /assets/ {
    add_header Cache-Control "max-age=600, stale-while-revalidate=3600, stale-if-error=86400";
}

2. CDN でのサポート状況

主要な CDN はこれらのヘッダーをサポートしており、より効果的に活用できます。

  • CloudFront: オリジンからの Cache-Control ヘッダーを尊重します。
  • Cloudflare: デフォルトで stale-while-revalidate が有効になっており、カスタム設定も可能です。
  • Fastly: stale-while-revalidatestale-if-error の両方をサポートしています。

SWR と SIE を同時に設定する実装例と検証

多くのケースでは、SWRSIE を併用することで、「初期表示の高速化」と「障害時の耐障害性」を同時に確保できます。

Cache-Control ヘッダー例

Cache-Control: max-age=60, stale-while-revalidate=3600, stale-if-error=86400
  • max-age=60: 60秒間は新鮮なデータとして扱う
  • stale-while-revalidate=3600: 期限切れ後3600秒間は古いデータを表示しながら裏で更新
  • stale-if-error=86400: エラー時は86400秒(1日)まで古いデータを使用

NGINX 設定例

location /api/ {
    add_header Cache-Control "max-age=60, stale-while-revalidate=3600, stale-if-error=86400";
}

検証方法

正常時(SWRの動作)

curl -I https://example.com/api/data

レスポンス例:

HTTP/2 200
cache-control: max-age=60, stale-while-revalidate=3600, stale-if-error=86400
cache-status: stale; hit; revalidated
  • stale → 古いキャッシュを使用
  • revalidated → バックグラウンドで更新中

障害時(SIEの動作)

HTTP/2 200
cache-control: max-age=60, stale-while-revalidate=3600, stale-if-error=86400
cache-status: stale; hit; error
  • error → オリジンから正常応答が得られずキャッシュ利用

stale-while-revalidateの実装注意点

各ヘッダーの役割と影響

ブラウザやプロキシは、複数のキャッシュ関連ヘッダーが競合する場合、最も厳格なキャッシュ無効化の指示を優先します。

したがって、stale-while-revalidateを設定する際は、Expires: 0Pragma: no-cacheといった、キャッシュを無効化するヘッダーを併用しないように注意する必要があります。これらを併用すると、stale-while-revalidateは期待通りに動作せず、キャッシュ無効化のルールが適用されます。Cache-Controlヘッダーでno-cacheno-storeを指定する場合も同様です。

GETとPOSTのキャッシュ挙動の違い

GETは、サーバーからデータを取得するためのメソッドです。サーバーの状態を変更しない「安全な」操作と見なされます。そのため、ブラウザやプロキシは、GETリクエストに対するレスポンスをキャッシュすることが許されています。Cache-Controlヘッダーにmax-agestale-while-revalidateが設定されていれば、その指示に従って積極的にキャッシュを再利用します。

対してPOSTは、サーバーにデータを送信して、サーバーの状態を変更する可能性がある「非安全な」操作です。例えば、フォームの送信や新しいリソースの作成などがこれに該当します。もしPOSTリクエストのレスポンスがキャッシュされてしまうと、同じリクエストを再度送信したときに、古いデータが表示されてしまうなど、予期せぬ副作用が発生する可能性があります。このリスクを避けるため、HTTPの仕様ではPOSTレスポンスはデフォルトでキャッシュされません


stale-if-error の運用注意点

SIE は耐障害性を高める強力なツールですが、ビジネスロジックによっては注意が必要です。

  • 古い情報のリスク: eコマースサイトの在庫情報や、価格情報など、リアルタイム性が重要なデータが長時間キャッシュされると、ユーザーに古い情報を提供してしまうリスクがあります。
  • 適切な期間設定: サービスやデータの特性を考慮し、SIE の期間を適切に設定することが重要です。

まとめ

SWR と SIE は、Web パフォーマンス改善可用性向上を両立させるための不可欠なHTTP キャッシュ戦略です。

  • SWR で初期表示を高速化
  • SIE でサーバー障害に備える
  • 併用設定 で双方のメリットを享受

これにより、ユーザーは常に快適で信頼性の高い体験を得ることができます。ぜひ、あなたのプロジェクトのレスポンスヘッダー設定にこれらのディレクティブを取り入れてみてください。ご覧いただきありがとうございました。


参考資料