HTTP キャッシュ完全ガイド:強力キャッシュ・検証キャッシュ・CDN 戦略を徹底解説

Web/SEO

HTTP キャッシュ:パフォーマンス最適化の第一防衛線

キャッシュを適切に設定すれば、2 回目以降のアクセスでネットワークリクエストがゼロになり、読み込み時間は秒単位からミリ秒単位へ短縮されます。

キャッシュ種別 仕組み サーバーへのリクエスト 速度
強力キャッシュ Cache-Control / Expires なし 最速(0ms)
検証キャッシュ ETag / Last-Modified あり(条件付きリクエスト) 速い(304 レスポンス)
キャッシュなし no-cache / no-store あり(完全リクエスト) 最も遅い

一、強力キャッシュ

Cache-Control ディレクティブ

Cache-Control: max-age=31536000, immutable
ディレクティブ 効果 適用シーン
max-age=N N 秒間キャッシュ 静的リソース
s-maxage=N CDN が N 秒間キャッシュ(max-age を上書き) CDN 環境
immutable 期限切れ後も再検証しない ハッシュ付きファイル名
public 中間キャッシュを許可 CDN
private ブラウザのみキャッシュ ユーザー個別のコンテンツ
no-cache 使用前に必ず検証 HTML ページ
no-store 一切キャッシュしない 機密データ
must-revalidate 期限切れ後は必ず検証 API レスポンス
stale-while-revalidate=N 期限切れ後は古い値を返しつつ裏で更新 段階的な更新

Expires(旧仕様)

Expires: Thu, 02 Jun 2027 00:00:00 GMT

優先順位:Cache-Control: max-age > Expires。


二、検証キャッシュ

ETag / If-None-Match

# 初回リクエスト
GET /api/data
→ 200 OK
  ETag: "abc123"
  Cache-Control: max-age=0

# 再リクエスト(条件付き)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified  (レスポンスボディなし、帯域を節約)

Last-Modified / If-Modified-Since

# 初回リクエスト
GET /style.css
→ 200 OK
  Last-Modified: Mon, 01 Jun 2026 12:00:00 GMT

# 再リクエスト
GET /style.css
If-Modified-Since: Mon, 01 Jun 2026 12:00:00 GMT
→ 304 Not Modified

ETag vs Last-Modified

観点 ETag Last-Modified
精度 バイト単位 秒単位
パフォーマンス ハッシュ計算が必要 ファイル時刻を読むだけ
信頼性 高い 低い(1 秒以内の複数変更を検知できない)
分散環境 ハッシュの一貫性が必要 本質的に一貫

推奨:ETag を優先する。


三、キャッシュ戦略マトリクス

HTML ページ

Cache-Control: no-cache

HTML はエントリーファイルであり、ユーザーが常に最新版を取得できるよう毎回検証が必要です。

ハッシュ付き静的リソース

Cache-Control: max-age=31536000, immutable

ファイル名にハッシュを含む場合(例:app.abc123.js)、内容が変わるとハッシュが変わり → URL が変わり → 新しいリクエストが発生します。古い URL は永久に変わらないため、1 年間キャッシュして immutable を付けられます。

ハッシュなし静的リソース

Cache-Control: max-age=86400, stale-while-revalidate=604800

期限切れ後は古い値を先に返し、裏で静かに更新するため、ユーザーは何も感じません。

API レスポンス

Cache-Control: private, max-age=0, must-revalidate

API レスポンスには通常ユーザーデータが含まれます。private で CDN のキャッシュを禁止し、must-revalidate で期限切れ後に古い値を返さないようにします。


四、Vary ヘッダー:キャッシュキーの拡張

Vary: Accept-Encoding, Accept-Language

Vary はキャッシュに対し「URL 以外にも、これらのリクエストヘッダーの値でキャッシュを区別せよ」と伝えます。

Vary の値 意味 シーン
Accept-Encoding 圧縮方式ごとにキャッシュ gzip vs br
Accept-Language 言語ごとにキャッシュ 多言語サイト
Cookie Cookie ごとにキャッシュ パーソナライズページ
* リクエストごとに独立してキャッシュ キャッシュしないのと同等

注意:Vary: Cookie はキャッシュヒット率を極端に下げるため、慎重に使いましょう。


五、CDN キャッシュ戦略

オリジン取得の制御

# CDN は 1 日、ブラウザは 5 分キャッシュ
Cache-Control: public, s-maxage=86400, max-age=300
シーン s-maxage max-age 説明
静的リソース 31536000 31536000 CDN・ブラウザともに長期キャッシュ
API レスポンス 300 0 CDN は 5 分キャッシュ、ブラウザはキャッシュしない
HTML 60 0 CDN は 1 分キャッシュ、ブラウザはキャッシュしない
個別コンテンツ — 0 CDN・ブラウザともにキャッシュしない

CDN キャッシュのパージ

方法 速度 適用
URL プリウォーム 速い 新コンテンツの公開
全量パージ 遅い 緊急修正
ディレクトリパージ 中程度 一括更新
Cache-Control: no-cache 自動 API シーン

Stale-While-Revalidate 実践

Cache-Control: max-age=60, stale-while-revalidate=86400
0 秒目:リクエスト → キャッシュ MISS → オリジン取得 → 新鮮な値を返す
30 秒目:リクエスト → キャッシュ HIT → キャッシュ値を返す
90 秒目:リクエスト → キャッシュ STALE → 古い値を即座に返す + 裏でオリジン取得
91 秒目:裏のオリジン取得が完了 → キャッシュ更新
120 秒目:リクエスト → キャッシュ HIT → 新しい値を返す

効果:ユーザーはオリジン取得を一切待たず、裏で静かに更新されます。


六、キャッシュバスティング戦略

コンテンツハッシュ(推奨)

<!-- ビルド時にハッシュを生成 -->
<script src="/app.abc123.js"></script>
<link href="/style.def456.css">
// webpack 設定
output: {
  filename: '[name].[contenthash:8].js',
}

バージョンクエリパラメータ

<!-- 簡単だが、CDN が ?v= パラメータをキャッシュしない場合がある -->
<script src="/app.js?v=2.0.0"></script>

問題点:一部の CDN はクエリパラメータを無視し、キャッシュが更新されないことがあります。

タイムスタンプ(非推奨)

<!-- 毎回変わるためキャッシュが完全に無効化される -->
<script src="/app.js?t=1717315200"></script>

七、Service Worker キャッシュ

Cache First 戦略

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => cached || fetch(event.request))
  );
});

Stale While Revalidate 戦略

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('dynamic').then(cache =>
      cache.match(event.request).then(cached => {
        const fetchPromise = fetch(event.request)
          .then(response => {
            cache.put(event.request, response.clone());
            return response;
          });
        return cached || fetchPromise;
      })
    )
  );
});

戦略の選び方

戦略 優先ソース 適用
Cache First キャッシュ フォント、画像
Network First ネットワーク API、HTML
Stale While Revalidate キャッシュ+裏で更新 CSS、JS
Network Only ネットワーク GET 以外のリクエスト
Cache Only キャッシュ オフラインページ

八、キャッシュのデバッグ

Chrome DevTools

  1. DevTools → Network パネル
  2. Size 列を確認:
    • from disk cache:ディスクキャッシュにヒット
    • from memory cache:メモリキャッシュにヒット
    • 実際のサイズ:キャッシュミス
  3. Age レスポンスヘッダーを確認:キャッシュが存在してからの経過時間
  4. Disable cache にチェック:キャッシュを無効化(DevTools 表示中のみ)

curl で検証

# レスポンスヘッダーを確認
curl -I https://example.com/style.css

# 条件付きリクエストを送信
curl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# 304 は検証キャッシュのヒットを示す

# 異なる Accept-Encoding をシミュレート
curl -I -H 'Accept-Encoding: br' https://example.com/style.css

九、ベストプラクティス チェックリスト

  1. HTML は no-cache:エントリーファイルを常に最新に
  2. ハッシュ付き JS/CSS は max-age=1 年 + immutable
  3. API は must-revalidate:期限切れ後は必ず検証
  4. CDN は s-maxage でオリジン取得の頻度を制御
  5. Vary: Accept-Encoding で圧縮方式を区別
  6. Last-Modified ではなく ETag を使う
  7. stale-while-revalidate でシームレスな更新を実現
  8. Service Worker でオフライン機能を補完
#HTTP缓存#Cache-Control#ETag#CDN#性能优化