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 が出たらすぐ見直す
  • 更新時の消去方法を決めてから本番に入れ、ページキャッシュ系プラグインとの二重化は避ける

お気軽にご相談ください

お見積りへ お問い合わせへ
▲