Nginx サーバーに HTTP キャッシュポリシーを設定することで、ブラウザや CDN などの中間プロキシに対し、画像、CSS、JS ファイルなどの静的アセットをキャッシュするよう指示できます。有効期間内であれば、キャッシュされたアセットはローカルのコピーから直接読み込まれるため、サーバーへのリクエストが不要になります。これにより、Web サイトの読み込み速度が向上し、サーバーの帯域幅が削減され、サーバー負荷が軽減されます。
一般的なキャッシュポリシーの例
このセクションでは、Nginx の設定ファイルに直接適用できる一般的なキャッシュ設定を紹介します。すべての例では、add_header ... always; ディレクティブを使用して、304 Not Modified レスポンスステータスコードを含むすべてのレスポンスにキャッシュヘッダーを追加します。
ユースケース 1:静的アセットの長期間キャッシュ
# 静的アセットの長期間キャッシュを設定
# - コンテンツハッシュを含むファイル名 (例:main.a1b2c3d4.js) の場合、'immutable' を指定した 1 年間のキャッシュを推奨します。
location ~* "\.[a-f0-9]{8,}\.(css|js|png|jpg|jpeg|gif|svg|webp|ico|woff|woff2)$" {
# ビルドツールによって生成されたハッシュを持つアセット:1 年間キャッシュし、ブラウザに再検証しないよう指示します。
add_header Cache-Control "public, max-age=31536000, immutable" always;
access_log off;
}
# - ファイル名が固定で変更頻度が低い (例:logo.png) 場合、30 日間のキャッシュを使用します。
location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|ico|woff|woff2)$ {
# ハッシュのない一般的な静的アセット:30 日間キャッシュし、CDN とブラウザの両方でのキャッシュを許可します。
add_header Cache-Control "public, max-age=2592000" always;
access_log off;
}ユースケース 2:HTML および SPA のキャッシュ
HTML ページ、特に index.html のようなシングルページアプリケーション (SPA) のエントリファイルは、新しいデプロイごとに頻繁に更新されます。そのため、これらのファイルに長期間のキャッシュを設定することは避けてください。パフォーマンス向上のため、ブラウザにこれらのファイルをキャッシュさせることはできますが、使用するたびにサーバーで再検証するよう要求する必要があります。
# 直接リクエストされた HTML ファイルの場合
location ~* \.html$ {
# ブラウザのキャッシュを許可しますが、使用するたびにサーバーでの再検証を要求します。
# 'private' ディレクティブは、中間プロキシ (CDN など) がこのレスポンスをキャッシュするのを防ぎます。
# サーバーは検証をサポートするために ETag または Last-Modified ヘッダーを提供する必要があります。
add_header Cache-Control "private, no-cache, must-revalidate" always;
}このポリシーでは、サーバーが
ETagまたはLast-Modifiedヘッダーを返す必要があります。これにより、ブラウザは条件付きリクエストを行うことができます。Nginx はデフォルトで静的ファイルにこれらのヘッダーを提供するため、追加の設定は不要です。CDN などの中間プロキシが HTML コンテンツをキャッシュするのを防ぐには、
privateディレクティブを使用します。これにより、エンドユーザーのブラウザのみがレスポンスをキャッシュすることが保証されます。
ユースケース 3:動的コンテンツおよび機密コンテンツのキャッシュ無効化
API エンドポイント、ユーザープロファイルページ、支払いページなど、動的に生成されるコンテンツや機密性の高いコンテンツについては、ブラウザや中間プロキシ (CDN や共有キャッシュなど) がレスポンスを保存しないようにします。これにより、データ漏洩や不整合を防ぐことができます。
# 例:動的な PHP スクリプトの場合 (設定に応じてパスを調整してください)
location ~ \.php$ {
# ... その他の PHP-FPM 設定 ...
# すべてのキャッシュを無効化:ブラウザ、プロキシ、CDN はレスポンスを保存してはなりません。
# 'no-store' は最も厳格なキャッシュ制御ディレクティブです。
add_header Cache-Control "no-store" always;
}クライアントキャッシュの設定
HTTP レスポンスに Cache-Control および Expires ヘッダーを追加することで、ブラウザのキャッシュを制御できます。
主要なディレクティブ
expiresディレクティブ:ExpiresヘッダーとCache-Controlヘッダーのmax-age値の両方を設定します。構文:
expires [time|epoch|max|off];例:
expires 30d;(リソースを 30 日間キャッシュ)、expires -1;(クライアントに使用前にサーバーでリソースを検証させる (Cache-Control: no-cacheに相当) が、リソースのキャッシュは許可する)。注意:
add_headerディレクティブは、より詳細な制御が可能なため推奨されます。
add_headerディレクティブ:指定された HTTP ヘッダーをレスポンスに追加します。構文:
add_header <name> <value> [always];alwaysパラメーターの説明
- デフォルトでは、add_headerは 2xx および 3xx 応答にのみ適用されます。 -304 Not Modified応答の場合、Nginx はカスタムヘッダーを自動的に追加しません。 ブラウザーは初回の 200 応答のキャッシュポリシーを再利用しますが、明確さと互換性を確保するために、キャッシュ制御ヘッダーにalwaysパラメーターを追加することを推奨します。 これにより、ヘッダーがすべての応答ステータスコードに適用されるようになります。
Cache-Control ディレクティブ
public:レスポンスは、ブラウザ、CDN、プロキシサーバーを含む任意のキャッシュに保存できます。private:レスポンスは、エンドユーザーのブラウザによってのみ保存でき、CDN のような共有キャッシュには保存できません。これは、ユーザー固有の情報を含むコンテンツに有用です。no-cache:クライアントは、キャッシュされたコピーを使用するたびに、サーバーにリクエストを送信して検証する必要があります。リソースが変更されていない場合、サーバーは304 Not Modifiedを返し、クライアントはローカルキャッシュを使用して帯域幅を節約できます。no-store:ブラウザやプロキシサーバーがレスポンスのいかなる部分も保存することを禁止します。これは、機密性の高いデータに適しています。max-age=<seconds>:キャッシュの生存期間 (TTL) を秒単位で設定します。immutable:アセットのコンテンツがキャッシュされている間は変更されないことをブラウザに伝えます。ファイル名にコンテンツハッシュが含まれるファイルに最適です。
デプロイと検証
設定の編集
サイトのserverブロックにlocationブロックを追加します。設定ファイルは通常、/etc/nginx/conf.d/または/etc/nginx/sites-enabled/にあります。設定のリロード
sudo nginx -t && sudo nginx -s reloadレスポンスヘッダーの検証
curlを使用してキャッシュヘッダーを確認します。この方法では、ブラウザのキャッシュをバイパスできます。curl -I http://your-domain.com/path/to/file.jsレスポンスには、
Cache-Control: public, max-age=31536000のような期待されるヘッダーが含まれているはずです。304の動作を検証 (no-cacheまたはETagベースのポリシーの場合)
検証ヘッダーを使用してテストします:ETAG=$(curl -I http://example.com/file.js 2>/dev/null | grep -i etag | cut -d' ' -f2 | tr -d '\r') curl -H "If-None-Match: $ETAG" -I http://example.com/file.js # 304 レスポンスが期待されますブラウザでの検証
デベロッパー ツールを開き、[ネットワーク] パネルに移動します。
初回ロードを確認するには、[キャッシュを無効にする] チェックボックスを選択します。ステータスは
200 OKになります。チェックボックスの選択を解除してページを更新します:
リクエストが行われない、または
(from cache)と表示される → 強力なキャッシュヒット304が返される → 条件付きキャッシュヒット
よくある質問
変更が反映されない
原因:
sudo nginx -s reloadコマンドが実行されていない。ブラウザ、CDN、またはプロキシサーバーが古いレスポンスを提供している。
locationブロックのマッチング優先順位が正しくない。
解決策:
curl -I http://your-urlを使用して、サーバーから直接レスポンスヘッダーを確認します。locationブロックの順序を確認します。正規表現マッチ (~* \.(css|js)$など) は、プレフィックスマッチ (/static/など) よりも優先されます。このため、リクエストが誤ったルールで処理されることがあります。
動的コンテンツが誤ってキャッシュされる
原因:静的アセットの正規表現が広すぎる (例:~* \.js$ が /api/user.js にマッチする)。
解決策:
パスを制限する:
location ~* ^/static/.*\.(css|js)$/api/や\.php$などの動的インターフェースに一致するlocationが優先的に一致するか、キャッシュから明示的に除外されていることを確認してください。
キャッシュポリシーの競合
原因:複数の
locationブロックが同じリクエストに一致するが、最初の 1 つしか有効にならない。解決策:
mapブロックを使用してポリシーを統合し、コンテンツタイプに基づいてキャッシュポリシーを動的に設定します。# http ブロック内 map $sent_http_content_type $cache_control { ~^image/ "public, max-age=2592000"; text/css "public, max-age=2592000"; application/javascript "public, max-age=2592000"; default "no-cache"; } # server ブロック内 add_header Cache-Control $cache_control always;