2026年10月5日
2026年10月5日
Nginxのキャッシュ設定でパフォーマンスを改善:静的ファイルとFastCGIキャッシュの使い分け
Nginx のキャッシュでサイトを速くするには、「ブラウザに静的ファイルを長く持たせる設定」と「PHP の実行結果を Nginx 自身が保存する FastCGI キャッシュ」の2つを、対象を分けて設定するのが効果的です。この記事では、それぞれの書き方と理由、WordPress でログイン中のユーザーやカート画面をキャッシュしないための除外条件、効いているかの確認方法、更新時の消し方までを説明します。
Nginx をリバースプロキシとして別のアプリケーションサーバーの前に置く構成(proxy_cache)は別記事で扱うため、ここでは Nginx と PHP-FPM が同じサーバーで動く一般的な WordPress 構成を前提にします。
まずキャッシュの層を整理する
Nginx の周辺で「キャッシュ」と呼ばれるものは複数あり、混同すると設定の効果を見誤ります。
- ブラウザキャッシュ:
Cache-ControlやExpiresヘッダーで、画像・CSS・JavaScript を訪問者の端末に保存させる。再訪時の通信が減る - FastCGI キャッシュ: PHP-FPM が返した HTML を Nginx がディスクに保存し、次の同じリクエストには PHP を通さずに返す。サーバーの負荷と応答時間(TTFB)が下がる
- open_file_cache: ファイルの存在やディスクリプタの情報を覚えておく仕組みで、中身のキャッシュではない
ページキャッシュ系のプラグインを使っている場合、FastCGI キャッシュと役割が重なります。両方を有効にすると、どちらの古いページが出ているのか切り分けにくくなるため、どちらか一方に寄せるのが運用しやすい形です。
静的ファイルにキャッシュヘッダーを付ける
画像やフォントは変更が少なく、長めに保存させても問題が起きにくい対象です。CSS と JavaScript は、WordPress がファイル名の後ろに ?ver=6.x のようなバージョン文字列を付けて読み込むため、更新時には URL が変わり、古いファイルが残り続ける心配は小さくなります。
location ~* \.(?:css|js|mjs)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
try_files $uri =404;
}
location ~* \.(?:jpg|jpeg|png|gif|webp|avif|svg|ico|woff2?)$ {
expires 90d;
add_header Cache-Control "public";
access_log off;
try_files $uri =404;
}
expires は Expires ヘッダーと Cache-Control: max-age の両方を出力します。ここで add_header を書くと、server ブロックに書いたセキュリティヘッダーがこの location では送られなくなる点に注意してください。必要なら同じヘッダーをここにも書くか、共通ファイルを include します。
テーマやプラグインがバージョン文字列なしで読み込むファイルを使っている場合、更新しても訪問者には古いものが表示されることがあります。そうしたファイルが多いサイトでは、期間を短めにして様子を見るのが無難です。
FastCGI キャッシュを設定する
保存場所とゾーンを定義する(http ブロック)
# /etc/nginx/conf.d/fastcgi-cache.conf(http ブロック内で読み込まれる場所)
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2
keys_zone=WORDPRESS:100m max_size=1g
inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
keys_zone=WORDPRESS:100mは、キャッシュのキー(索引)を保持する共有メモリの名前と大きさです。中身の HTML はディスクに置かれますmax_sizeはディスク上の上限、inactiveは一定時間アクセスがなければ期限に関係なく消す設定です- キーに
$schemeを含めないと、HTTP と HTTPS で同じキャッシュを共有し、リダイレクトの誤配信が起きることがあります
保存先のディレクトリは Nginx のワーカーが書き込める必要があります。権限エラーは error.log に記録されるので、設定後に一度確認します。
キャッシュしない条件を決める(server ブロック)
WordPress では、ログイン中の画面やコメント投稿者向けの表示、カートや決済の画面をキャッシュすると、別の人に個人の情報が見えてしまう危険があります。除外条件の設計が、この設定でいちばん大切な部分です。
set $skip_cache 0;
# POST やクエリ文字列つきのリクエストは対象外
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
# 管理画面・ログイン・フィード・XML-RPC など
if ($request_uri ~* "/wp-admin/|/wp-login\.php|/xmlrpc\.php|/wp-json/|/feed/|sitemap(_index)?\.xml") {
set $skip_cache 1;
}
# ログイン中・コメント投稿者・パスワード保護ページを見た人
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
set $skip_cache 1;
}
# WooCommerce を使う場合のカート・決済・マイアカウント
if ($request_uri ~* "/cart/|/checkout/|/my-account/") { set $skip_cache 1; }
if ($http_cookie ~* "woocommerce_items_in_cart|wp_woocommerce_session") { set $skip_cache 1; }
if の中で set だけを行う使い方は、Nginx の if の中でも問題が起きにくい形です。WooCommerce のページのスラッグは設定で変えられるため、自分のサイトの URL に合わせて書き換えてください。会員制プラグインや多言語プラグインを使っている場合も、それらが使う Cookie を除外に加える必要があります。
PHP の location でキャッシュを使う
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock; # 環境のソケット名に合わせる
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_lock on;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
add_header X-Cache-Status $upstream_cache_status always;
}
fastcgi_cache_bypassは「キャッシュを読まない」、fastcgi_no_cacheは「結果を保存しない」です。両方に同じ変数を渡すのが基本ですfastcgi_cache_validの時間を長くするほど負荷は下がりますが、更新が反映されるまでの遅れも長くなります。まずは数分〜10分程度から始め、更新頻度に合わせて調整しますfastcgi_cache_lockは、同じページへのアクセスが集中したときに PHP を1回だけ実行させる設定ですfastcgi_cache_use_staleとbackground_updateで、期限切れの間も古いページを返しつつ裏で更新します
PHP が Set-Cookie ヘッダーを返した応答は、既定ではキャッシュされません。この挙動を fastcgi_ignore_headers で無効にすると、誰かのセッション Cookie を他人に配るおそれがあるため、理由が明確でない限り変更しないでください。
効いているかを確認する
sudo nginx -t && sudo systemctl reload nginx
# 同じ URL に2回アクセスし、X-Cache-Status を見る
curl -sI https://example.com/sample-post/ | grep -i x-cache-status
curl -sI https://example.com/sample-post/ | grep -i x-cache-status
1回目が MISS、2回目が HIT になれば保存と配信ができています。BYPASS は除外条件に当たったことを意味します。ログイン状態のブラウザで HIT が出る場合は除外の Cookie 条件が合っていないため、すぐにキャッシュを止めて見直してください。
応答時間の変化は、curl -o /dev/null -s -w '%{time_starttransfer}\n' URL で TTFB を何回か測ると比べやすくなります。ページ表示全体の評価は、PageSpeed Insights などのツールで改善の方向を確認します。
記事更新時のキャッシュの消し方
オープンソース版の Nginx には、キャッシュを URL 単位で消す標準のディレクティブがありません。選択肢は次のとおりです。
- 有効期限を短めにして、自然に入れ替わるのを待つ
- 保存ディレクトリの中身を削除する(
sudo find /var/cache/nginx/wordpress -type f -delete)。全ページが一度MISSになるため、アクセスの多い時間帯は避ける - WordPress のプラグイン「Nginx Helper」を使い、投稿の更新時に該当ページのキャッシュを消す。消去の方式(ファイルの削除か、サードパーティの purge モジュールを使うか)や設定項目の名前は版によって変わるため、導入時にプラグインの説明を確認してください(2026年10月現在)
いずれの方法でも、導入前にステージング環境で「記事を更新したら数分以内に反映されるか」「ログイン中は常に最新が見えるか」を確かめてから本番に適用すると安心です。
まとめ
- 静的ファイルは
expiresでブラウザに保存させ、HTML は FastCGI キャッシュで PHP の実行を省く fastcgi_cache_keyに$schemeを含め、HTTP と HTTPS のキャッシュを分ける- ログイン中・コメント投稿者・カートや決済の画面は Cookie と URL の両面で除外する
X-Cache-Statusヘッダーで HIT・MISS・BYPASS を確認し、ログイン時に HIT が出たらすぐ見直す- 更新時の消去方法を決めてから本番に入れ、ページキャッシュ系プラグインとの二重化は避ける