HTTPキャッシュの解説:ETag、Cache-Control、CDN
なぜあなたのウェブサイトは遅く感じるのか(そしてキャッシュがそれをどう解決するか)
画像を最適化し、CSSを圧縮し、サーバーまでアップグレードした。それなのに、リピーターの読み込み時間は依然として遅く、オリジンサーバーは帯域幅を消費している。原因は? 非効率なHTTPキャッシュです。適切なキャッシュヘッダーがなければ、ブラウザは訪問のたびに同じアセットを再ダウンロードし、CDNも効果的に機能できません。
HTTPキャッシュは、利用可能な中で最も効果の高いパフォーマンス最適化の1つです。レイテンシを削減し、帯域幅コストを抑え、オリジンサーバーの負荷を軽減します。このガイドでは、主要なメカニズムであるETag、Cache-Control、そしてCDNがどのように関わるかを解説します。正しく実装するための実践的な戦略を学べます。
HTTPキャッシュの仕組み:全体像
ブラウザがリソースをリクエストすると、オリジンサーバーから取得するか、ローカルに保存されたコピーを提供するかを選択できます。HTTPキャッシュは、保存されたコピーがいつ新鮮と見なされ、いつ再検証が必要かを定義するルールです。
主なキャッシュの種類は2つあります:
- ブラウザキャッシュ(プライベート): ユーザーのブラウザがリソースをローカルに保存します。これは複数のページや訪問にわたって単一のユーザーに利益をもたらします。
- 共有キャッシュ(CDN、プロキシ): 中間サーバーが複数のユーザーに対してリソースをキャッシュします。これによりオリジンの負荷が減り、グローバルな配信が高速化されます。
どちらもHTTPヘッダーに依存して、新鮮さと再検証を決定します。最も重要な2つのヘッダーはCache-ControlとETagです。
Cache-Control:新鮮さのルール
Cache-Controlは、キャッシュポリシーを定義する主要なヘッダーです。これはディレクティブベースのヘッダーで、キャッシュにレスポンスの扱い方を指示します。
主要なディレクティブ
- max-age:レスポンスが新鮮と見なされる秒数。例えば、
Cache-Control: max-age=3600はレスポンスが1時間新鮮であることを意味します。 - s-maxage:max-ageと同様ですが、共有キャッシュ(CDN)専用です。共有キャッシュではmax-ageを上書きします。
- public:レスポンスは共有キャッシュを含む任意のキャッシュでキャッシュ可能です。
- private:レスポンスは単一のユーザー向けであり、共有キャッシュに保存されるべきではありません。
- no-cache:レスポンスは保存できますが、使用前に毎回オリジンで再検証する必要があります。
- no-store:レスポンスはどのキャッシュにも保存してはなりません。機密データに使用します。
- must-revalidate:一度古くなったら、キャッシュは再検証なしにレスポンスを使用してはなりません。
- immutable:レスポンスはその新鮮期間中に変更されません。バージョン付きアセットに便利です。
例:Cache-Control: public, max-age=31536000, immutableは、ハッシュ化されたファイル名を持つ静的アセットに最適です。
ETagと条件付きリクエスト
ETag(エンティティタグ)は、リソースの特定のバージョンの識別子です。リソースが変更されると、ETagも変わります。ブラウザはETagを使用して条件付きリクエストを行います:保存されたETagをIf-None-Matchヘッダーで送信します。リソースが変更されていなければ、サーバーは304 Not Modifiedをボディなしで返し、帯域幅を節約します。
同様に、Last-ModifiedはIf-Modified-Sinceと連携しますが、ETagの方がより正確です(同じ秒内の変更も検出できます)。
ETagの生成方法
ほとんどのウェブサーバーとフレームワークはETagを自動生成します。例えば、Express.jsではapp.set('etag', 'strong')で有効にできます。Nginxでは、静的ファイルに対してデフォルトでETagが有効です。
強力なETag(例:"abc123")はバイト単位での同一性を保証します。弱いETag(例:W/"abc123")は意味的な等価性を示し、正確なバイトを保証しません。
CDNキャッシュ:大規模な共有キャッシュ
CDN(コンテンツデリバリネットワーク)は、グローバルに分散された共有キャッシュとして機能します。エッジロケーションでコンテンツをキャッシュし、ユーザーに近い拠点から配信します。これによりレイテンシが減り、オリジンの負荷が軽減されます。
CDNはCache-Controlヘッダーを尊重しますが、多くの場合独自の設定を持っています。主な概念:
- エッジキャッシュ: CDNのローカルキャッシュ。キャッシュキー(通常はURL +
Accept-Encodingなどのヘッダー)に基づいてレスポンスを保存します。 - オリジンシールド: オリジンへのリクエストを減らす追加のキャッシュ層。
- キャッシュ無効化: CDNはリソースを更新したときにキャッシュされたコンテンツをパージするAPIを提供します。
CDNを使用する場合、Cache-Controlにs-maxageを設定して、ブラウザキャッシュとは別に共有キャッシュの新鮮さを制御します。例えば:Cache-Control: public, max-age=600, s-maxage=3600は、ブラウザは10分間キャッシュし、CDNは1時間キャッシュすることを意味します。
キャッシュヘッダーの比較
| ヘッダー | 目的 | 例 |
|---|---|---|
Cache-Control |
新鮮さとキャッシュルールを定義 | public, max-age=3600 |
ETag |
リソースバージョンの一意の識別子 | "abc123" |
Last-Modified |
最終変更のタイムスタンプ | Wed, 21 Oct 2025 07:28:00 GMT |
Expires |
レガシーな絶対有効期限 | Wed, 21 Oct 2025 07:28:00 GMT |
Vary |
キャッシュに影響するヘッダーを指定 | Accept-Encoding |
注:ExpiresはCache-Controlに取って代わられましたが、古いクライアントではまだ使用されています。
ウェブアプリのための実践的なキャッシュ戦略
効果的なキャッシュを実装するには、次の手順に従ってください:
- 静的アセットにフィンガープリントを付ける: ハッシュ化されたファイル名(例:
app.a1b2c3.js)を使用し、長いmax-ageとimmutableを設定します。ファイルが変更されるとハッシュが変わり、キャッシュが更新されます。 - HTMLに適切なCache-Controlを設定する: HTMLは通常
no-cacheまたは短いmax-ageにして、ユーザーがすぐに更新を受け取れるようにします。再検証にはETagを使用します。 Vary: Accept-Encodingを使用する: 圧縮版と非圧縮版を配信する場合、これによりキャッシュが別々に保存します。- s-maxageでCDNを活用する: 共有キャッシュには長めの
s-maxageを設定してオリジンの負荷を減らし、必要に応じてブラウザキャッシュは短く保ちます。 - 賢く無効化する: 重要な更新をデプロイするときはCDNのパージAPIを使用します。静的アセットでは、フィンガープリントによりパージの必要がなくなります。
- キャッシュヒット率を監視する: CDNの分析を使用して、キャッシュが効果的であることを確認します。ヒット率が低い場合は、ヘッダーの設定ミスを示しています。
よくある落とし穴と回避方法
- HTMLの過剰キャッシュ: ユーザーに古いコンテンツが表示されます。
no-cacheまたは短いmax-ageを使用してください。 - 静的アセットのキャッシュ不足: フィンガープリントと共に長い
max-age(例:1年)を設定します。 Varyの無視: キャッシュが誤ったコンテンツ(例:gzip vs プレーン)を配信する可能性があります。常にVary: Accept-Encodingを設定してください。- ユーザー固有データに
privateを忘れる: 共有キャッシュがデータを漏洩する可能性があります。認証済みレスポンスにはCache-Control: privateを使用します。 no-storeの誤用: すべてのキャッシュを防ぐため、パフォーマンスを損なう可能性があります。機密データにのみ使用してください。
キャッシュ設定のテスト
ブラウザのDevTools(ネットワークタブ)を使用してレスポンスヘッダーを検査し、リソースがキャッシュから提供されているか(「(from disk cache)」または「(from memory cache)」を探す)を確認します。CDNの動作については、curl -Iを使用してX-CacheやCF-Cache-Statusなどのヘッダーを確認します。WebPageTestなどのツールは、訪問間のキャッシュを視覚化できます。
FAQ
ETagとLast-Modifiedの違いは何ですか?
ETagはリソースが変更されると変わる不透明な識別子で、Last-Modifiedはタイムスタンプです。ETagは同じ秒内の変更も検出でき、クロック同期に依存しないため、より正確です。
no-cacheとno-storeはいつ使うべきですか?
キャッシュにレスポンスを保存させたいが、使用前に毎回オリジンで再検証したい場合はno-cacheを使用します。どのキャッシュにもディスクやメモリに書き込まれてはならない機密データにはno-storeを使用します。
CDNはキャッシュ無効化をどのように処理しますか?
CDNは、特定のURLやディレクトリ全体をエッジキャッシュから削除できるパージAPIを提供します。ソフトパージをサポートし、コンテンツを古いものとしてマークし、次のリクエストで再検証するものもあります。アセットのフィンガープリントは、パージよりも効率的なことが多いです。
結論
ETag、Cache-Control、CDNを使ったHTTPキャッシュをマスターすることは、高速でスケーラブルなウェブアプリケーションを構築するために不可欠です。静的アセットとHTMLに適切なヘッダーを設定することから始め、s-maxageでCDNの共有キャッシュを活用し、常に設定をテストしましょう。キャッシュヘッダーの小さな変更が、大きなパフォーマンス向上につながります。
サーバーログをすばやく分析してキャッシュヒット率を確認したいですか? Nginx Log Analyzerを使ってアクセスログを解析・可視化しましょう。