【nginxキャッシュ完全攻略】ブラウザキャッシュとproxy_cacheの仕組み・設定・使い分け

サーバー

はじめに

Webアプリを速くするには「どこで」キャッシュするかが鍵です。nginxには大きく2種類のキャッシュ戦略があります。クライアント側に保存するブラウザキャッシュと、nginxサーバ自身が保存するプロキシキャッシュ(サーバキャッシュ)です。それぞれの仕組み・設定方法・使い分けを解説します。

キャッシュの基本フロー

リクエストがどこでキャッシュされるかを確認しましょう。

① ブラウザキャッシュHIT(最速)

ブラウザ → ローカルキャッシュ (ネットワーク不使用)

② サーバキャッシュHIT

ブラウザ → nginx → proxy_cache (オリジン不使用)

③ キャッシュMISS(フルリクエスト)

ブラウザ → nginx → キャッシュなし → オリジンサーバ

ブラウザキャッシュ

ブラウザキャッシュは、nginxがHTTPレスポンスヘッダに指示を書き込み、ブラウザに「このファイルはN秒間ローカルに保存してよい」と伝える仕組みです。次回アクセス時にネットワークを一切使わずに表示できるため、体感速度の改善に直結します。

主要ディレクティブ:expires と Cache-Control

# 静的アセット向け設定例
server {
    location ~* \.(js|css|png|jpg|svg|woff2)$ {
        expires        1y;
        add_header     Cache-Control "public, immutable";
    }


    location ~* \.html$ {
        expires        -1;
        add_header     Cache-Control "no-cache";
    }
}

immutable ディレクティブは「有効期限内はリロードしても再検証しない」という指示です。ファイル名にハッシュを含む場合(例:main.a3f9b.js)と組み合わせると最も効果的です。

Cache-Control の主な値

キャッシュを許可する値

  • public — CDNも含めキャッシュ可
  • max-age=N — N秒間キャッシュ
  • immutable — 期限内は再検証不要
  • stale-while-revalidate — 古いキャッシュを返しつつ更新

キャッシュを制限する値

  • no-cache — 毎回サーバに再検証
  • no-store — 一切保存しない
  • private — ブラウザのみ可(CDN不可)
  • must-revalidate — 期限切れなら必ず検証

ETag / Last-Modified による条件付きリクエスト

no-cache を指定した場合、ブラウザは毎回サーバに確認します。このとき ETag や If-Modified-Since ヘッダを使った条件付きリクエストが発生し、変更がなければ 304 Not Modified が返るため転送量を節約できます。

# nginxデフォルトでETagは有効。無効化したい場合:
etag off;


# Last-Modifiedも合わせて制御する場合:
add_header Last-Modified "";
if_modified_since off;

注意:APIレスポンスや個人情報を含むページには必ず Cache-Control: no-store, private を設定してください。誤って public にするとCDNや共有プロキシに機密情報が残るリスクがあります。

サーバキャッシュ(proxy_cache)

nginxをリバースプロキシとして使う場合、バックエンドのレスポンスをnginx自身がディスクにキャッシュできます。バックエンドへのリクエスト数を劇的に減らせるため、サーバ負荷軽減とスループット向上に効果的です。

基本設定

# http ブロックでキャッシュゾーンを定義
http {
    proxy_cache_path  /var/cache/nginx
                      levels=1:2
                      keys_zone=my_cache:10m
                      max_size=1g
                      inactive=60m
                      use_temp_path=off;
}
# server / location ブロックで有効化
server {
    location / {
        proxy_pass             http://backend;
        proxy_cache            my_cache;
        proxy_cache_valid      200 302  10m;
        proxy_cache_valid      404      1m;
        proxy_cache_use_stale  error timeout updating;
        proxy_cache_lock       on;
        add_header             X-Cache-Status $upstream_cache_status;
    }
}

キャッシュステータスを確認する

X-Cache-Status ヘッダにより、DevToolsで各リクエストのキャッシュ状態を確認できます。

  • HIT — キャッシュから返却
  • MISS — バックエンドにリクエスト
  • STALE — 期限切れキャッシュを使用
  • BYPASS — キャッシュをスキップ

キャッシュのバイパスとパージ

# Cookieやクエリパラメータによってキャッシュをスキップ
proxy_cache_bypass  $cookie_session $arg_nocache;
proxy_no_cache      $cookie_session $arg_nocache;


# PURGEメソッドでキャッシュを即時削除(ngx_cache_purge モジュール)
location ~ /purge(/.*)  {
    allow             127.0.0.1;
    deny              all;
    proxy_cache_purge my_cache $scheme$proxy_host$1;
}

proxy_cache_lock on を設定すると、同じURLへの同時リクエストが来た際にバックエンドへ流れるのを1リクエストだけに絞れます(Thundering Herd 問題の防止)。

2つのキャッシュの違い

ブラウザキャッシュとサーバキャッシュの主な違いを表にまとめます。

項目 ブラウザキャッシュ サーバキャッシュ
保存場所 ユーザのブラウザ nginxサーバのディスク
主な用途 静的ファイル(JS/CSS/画像) 動的コンテンツ、APIレスポンス
制御方法 Cache-Control / Expires ヘッダ proxy_cache_* ディレクティブ
即時無効化 難しい(URLを変える必要がある) PURGEリクエストで即時削除可
効果の対象 個々のユーザの体感速度 全ユーザへのバックエンド負荷

実践的な組み合わせパターン

最も効果的なのは両方を組み合わせることです。静的ファイルはブラウザキャッシュ、動的コンテンツはサーバキャッシュでバックエンドを保護しましょう。

# 推奨パターン:ハッシュ付き静的ファイル
location ~* \.(js|css)$ {
    expires    1y;
    add_header Cache-Control "public, immutable";
    gzip_static on;
}


# HTMLはno-cache(常に最新のハッシュURLを持つhtmlを取得)
location ~* \.html$ {
    add_header Cache-Control "no-cache";
    proxy_cache       my_cache;
    proxy_cache_valid 200 5m;
}


# 共通APIはサーバキャッシュ
location /api/public/ {
    proxy_cache       my_cache;
    proxy_cache_valid 200 1m;
    add_header        Cache-Control "no-store";
}


# プライベートAPIはキャッシュなし
location /api/private/ {
    proxy_cache_bypass 1;
    add_header         Cache-Control "no-store, private";
}

デプロイ時のポイント:静的ファイルにはビルド時にコンテンツハッシュを付与(webpack や Vite のデフォルト動作)することで、Cache-Control: immutable, max-age=31536000 を安全に使えます。ユーザは自動的に新しいURLにアクセスするため、キャッシュの強制削除が不要になります。