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

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)する」キャッシュ戦略です。
- 高速な初期表示: ユーザーは古いキャッシュを即座に見られるため、待機時間がありません。
- 常に最新のデータ: 裏側で最新データを取得し、次回アクセス時に更新されます。
Cache-Control ヘッダー例
Cache-Control: max-age=60, stale-while-revalidate=3600
この設定により、60秒間は最新データとして扱い、その後 3600秒間は古いデータを表示しつつ、バックグラウンドで再検証が行われます。
stale-if-error
(SIE)
SIE は「エラーが発生した場合に限り、古い(stale)データを表示する」キャッシュ戦略です。
- サービスの可用性維持: オリジンサーバーが応答しなかったり、ネットワークエラーが発生したりした場合でも、キャッシュされたデータが表示され、ユーザーは白画面を見ることがなくなります。
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-revalidate
とstale-if-error
の両方をサポートしています。
SWR と SIE を同時に設定する実装例と検証
多くのケースでは、SWR と SIE を併用することで、「初期表示の高速化」と「障害時の耐障害性」を同時に確保できます。
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: 0
やPragma: no-cache
といった、キャッシュを無効化するヘッダーを併用しないように注意する必要があります。これらを併用すると、stale-while-revalidate
は期待通りに動作せず、キャッシュ無効化のルールが適用されます。Cache-Control
ヘッダーでno-cache
やno-store
を指定する場合も同様です。
GETとPOSTのキャッシュ挙動の違い
GET
は、サーバーからデータを取得するためのメソッドです。サーバーの状態を変更しない「安全な」操作と見なされます。そのため、ブラウザやプロキシは、GET
リクエストに対するレスポンスをキャッシュすることが許されています。Cache-Control
ヘッダーにmax-age
やstale-while-revalidate
が設定されていれば、その指示に従って積極的にキャッシュを再利用します。
対してPOST
は、サーバーにデータを送信して、サーバーの状態を変更する可能性がある「非安全な」操作です。例えば、フォームの送信や新しいリソースの作成などがこれに該当します。もしPOST
リクエストのレスポンスがキャッシュされてしまうと、同じリクエストを再度送信したときに、古いデータが表示されてしまうなど、予期せぬ副作用が発生する可能性があります。このリスクを避けるため、HTTPの仕様ではPOST
レスポンスはデフォルトでキャッシュされません。
stale-if-error
の運用注意点
SIE は耐障害性を高める強力なツールですが、ビジネスロジックによっては注意が必要です。
- 古い情報のリスク: eコマースサイトの在庫情報や、価格情報など、リアルタイム性が重要なデータが長時間キャッシュされると、ユーザーに古い情報を提供してしまうリスクがあります。
- 適切な期間設定: サービスやデータの特性を考慮し、SIE の期間を適切に設定することが重要です。
まとめ
SWR と SIE は、Web パフォーマンス改善と可用性向上を両立させるための不可欠なHTTP キャッシュ戦略です。
- SWR で初期表示を高速化
- SIE でサーバー障害に備える
- 併用設定 で双方のメリットを享受
これにより、ユーザーは常に快適で信頼性の高い体験を得ることができます。ぜひ、あなたのプロジェクトのレスポンスヘッダー設定にこれらのディレクティブを取り入れてみてください。ご覧いただきありがとうございました。
参考資料
- MDN Web Docs: Cache-Control:
stale-while-revalidate
やstale-if-error
の詳細な仕様について解説されています。 - RFC 5861: SWR/SIE の公式な仕様を定めたドキュメントです。
- HTTP キャッシュ入門: Web のキャッシュ全般について学びたい方向け。