2026年5月28日
2026年5月28日
WordPressのサーバー(ノード)側にキャッシュを導入してTTFBを改善する方法
WordPressのTTFB(Time to First Byte)が遅い原因の多くは、リクエストのたびにPHPがページを組み立て、データベースに問い合わせていることです。サーバー(ノード)側にページキャッシュを置き、PHPを通さずにHTMLを返せるようにすると、キャッシュに当たったリクエストのTTFBは大きく縮みます。この記事では、計測から始めて、Nginxのページキャッシュ、オブジェクトキャッシュ、「Node.jsをキャッシュ層に使う構成」の是非までを、どこから手を付けるべきかの順に説明します。
CDN側のキャッシュ設定とオリジンの役割分担は別の話題として扱うので、ここでは「オリジンのサーバー1台(または各ノード)で何をするか」に絞ります。
まずTTFBを測って、遅い場所を特定する
対策の前に、どこで時間がかかっているかを数字で把握します。ブラウザの開発者ツールのネットワークタブでHTMLのリクエストを選ぶと「待機時間(TTFB)」が見られますが、サーバー側の変化を比べるにはcurlが手軽です。
# 同じURLを5回測り、TTFB(time_starttransfer)を秒で表示
for i in 1 2 3 4 5; do
curl -o /dev/null -s -w "%{time_starttransfer}\n" https://example.com/
done
1回目だけ遅く2回目以降が速いなら、すでに何らかのキャッシュが効いています。毎回同じように遅いなら、ページの生成そのものが重いということです。
web.devでは、TTFBはおおむね0.8秒以下を目安としていますが、これはあくまで目安で、Core Web Vitalsの指標そのものではありません。基準は更新されることがあるため、執筆時点の値として参照してください(2026年10月現在)。大事なのは、改善前後を同じ条件で比べることです。ログイン中はキャッシュを通らない構成が普通なので、計測はログアウトした状態(シークレットウィンドウやcurl)で行います。
ページ生成の内訳を知りたい場合は、Query Monitorプラグインで遅いクエリやHTTP API呼び出しを確認できます。外部APIの呼び出しが毎回走っているなら、キャッシュより先にその処理を見直すほうが効果的なこともあります。
キャッシュの層を整理する
サーバー側でTTFBに効くキャッシュは、主に次の3層です。効果が大きく、副作用も大きい順に並べています。
- ページキャッシュ:完成したHTMLを保存し、次のリクエストではPHPを起動せずに返す。TTFBへの効果が最も大きい
- オブジェクトキャッシュ:Redisなどに
WP_Object_Cacheの内容を保存し、DBへの問い合わせを減らす。ログイン中の管理画面やカート画面など、ページキャッシュできない画面にも効く - OPcache:PHPのコンパイル結果をメモリに保持する。多くの環境で既定で有効だが、無効になっていないか確認する価値はある
OPcacheが有効かは php -i | grep opcache.enable か、管理画面の「サイトヘルス」の情報タブで確認できます。WebサーバーとCLIで設定が別の場合があるので、可能ならPHP-FPM側の設定を見てください。
Nginxのページキャッシュ(fastcgi_cache)を設定する
Nginx+PHP-FPMの構成なら、Nginx標準の fastcgi_cache でページキャッシュを作れます。作業前に設定ファイルをバックアップし、ステージング環境で試してから本番に反映してください。
# http ブロック内(例: /etc/nginx/conf.d/fastcgi-cache.conf)
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2
keys_zone=WPCACHE:64m max_size=1g inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# server ブロック内
set $skip_cache 0;
# GET/HEAD 以外、クエリ文字列付きはキャッシュしない
if ($request_method !~ ^(GET|HEAD)$) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
# 管理画面・ログイン・REST API・フィードなどは対象外
if ($request_uri ~* "^/(wp-admin/|wp-login\.php|wp-json/|xmlrpc\.php|wp-cron\.php)|/feed/") {
set $skip_cache 1;
}
# ログイン中・コメント投稿者・パスワード保護ページの閲覧者・カートに商品がある人は対象外
if ($http_cookie ~* "wordpress_logged_in_|comment_author_|wp-postpass_|woocommerce_items_in_cart") {
set $skip_cache 1;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_cache WPCACHE;
fastcgi_cache_valid 200 301 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
# 同時アクセスで PHP に処理が集中しないようにする
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;
}
設定のねらいは次のとおりです。
- 除外条件が本体です。ログイン中のユーザーにキャッシュされたHTMLを返すと、管理バーが出ない程度ならまだしも、別の人向けの内容(カートや会員情報)が表示される事故につながります。WooCommerceや会員プラグインを使っている場合は、そのプラグインが使うCookieや、マイページ・カート・決済のURLも除外に加えてください。
fastcgi_cache_lockは、キャッシュが切れた瞬間に大量のリクエストが同時にPHPへ流れ込むのを防ぎます。fastcgi_cache_use_staleとfastcgi_cache_background_updateは、期限切れのキャッシュを返しながら裏で更新する設定で、PHPが一時的に落ちても古いページを返し続けられます。fastcgi_passのソケットパスは環境によって違います(例:php8.3-fpm.sock)。既存の設定に合わせてください。- Nginxの
add_headerは、location内に1つでも書くと、server側に書いたセキュリティヘッダーなどが継承されなくなります。server側で付けているヘッダーがある場合は、location内にも同じものを書くか、共通ファイルをincludeしてください。
反映前に構文を確認し、問題なければ再読み込みします。
sudo nginx -t && sudo systemctl reload nginx
curl -sI https://example.com/ | grep -i x-cache-status
1回目が MISS、2回目が HIT になれば動作しています。ログイン状態のブラウザで BYPASS になることも確認してください。
投稿を更新したときにキャッシュを消す
fastcgi_cache_valid の期限が来るまで、記事を更新しても古いページが表示されます。対処は2通りです。
- 期限を短めにする:5〜10分程度にしておけば、更新の反映待ちは許容できることが多い
- パージの仕組みを入れる:Nginx Helperプラグインは、投稿の更新時にキャッシュを削除する機能を持っています。ただし、Nginx標準にはURL単位のパージ機能がなく、
ngx_cache_purgeモジュールを組み込むか、プラグインが対応するファイル削除方式を使う必要があります。対応方式はプラグインのバージョンで変わるので、導入前に説明を確認してください
キャッシュディレクトリを丸ごと削除する運用も可能ですが、削除直後はキャッシュが空になり、一時的にPHPの負荷が上がる点に注意します。
プラグインやホスティングのキャッシュを使う場合
サーバーの設定を触れない共用サーバーでは、ページキャッシュ系のプラグイン(WP Super Cache、LiteSpeed Cacheなど)や、ホスティング会社が提供するサーバーキャッシュ機能を使います。LiteSpeed CacheはLiteSpeed系のWebサーバーでないと主要機能が働かないなど、サーバーとの相性があります。ページキャッシュは1か所に絞るのが原則で、サーバー側とプラグイン側の両方で有効にすると、どちらが古いページを返しているのか分からなくなります。
オブジェクトキャッシュ(Redis)を追加する
ページキャッシュはログイン中の画面や、カート・検索結果など人によって内容が変わるページには効きません。こうした画面のTTFBを改善したい場合は、Redisを使ったオブジェクトキャッシュを追加します。Redisを同じサーバーに入れ、Redis Object Cacheプラグインを有効化すると、wp_cache_get() などのWordPressのキャッシュAPIを通るデータがリクエストをまたいで保持されます。
効果はサイトによって差が大きく、DBへの問い合わせが少ない小さなサイトでは体感できないこともあります。Query MonitorでクエリやTTFBの変化を見ながら判断してください。
Node.jsをキャッシュ層に使う構成はどうか
「Nodeキャッシュ」という言葉で、Node.jsのサーバーをWordPressの前に置いてキャッシュさせる構成を想像する人もいます。技術的には可能ですが、通常のWordPressサイトではおすすめしません。前述のCookieによる除外、期限切れ時の同時アクセス対策、障害時に古いページを返す処理などを自前で作ることになり、Nginxの fastcgi_cache やVarnishが既に持つ機能を作り直すだけになりがちだからです。
Node.jsが意味を持つのは、WordPressをAPI(REST APIやWPGraphQL)として使い、表示をNext.jsなどのフロントエンドで行うヘッドレス構成の場合です。この場合はフロントエンド側でページを生成・キャッシュし、WordPressのTTFBはAPIの応答速度として扱います。フレームワークのキャッシュの既定値はバージョンによって変わってきた経緯があるため、採用する場合は使うバージョンのドキュメントで挙動を確認してください。
複数ノード構成での注意
ロードバランサーの後ろにWebサーバーが複数台ある場合、fastcgi_cache は各ノードが個別に持つことになります。ノードごとにキャッシュの状態がずれるので、パージは全ノードに対して行う必要があります。台数が増えるなら、ページキャッシュはCDNや共有のキャッシュ層に寄せ、各ノードはオブジェクトキャッシュ(共有のRedis)を使う構成のほうが管理しやすくなります。
まとめ
- まずログアウト状態でTTFBを計測し、毎回遅いのか、初回だけ遅いのかを見分ける
- 効果が大きいのはページキャッシュ。Nginxなら
fastcgi_cacheで、Cookieと管理系URLの除外が設定の中心 fastcgi_cache_lockとuse_staleで、期限切れや障害時のPHPへの集中を抑える- 更新の反映は短めの期限かパージの仕組みで対応し、ページキャッシュは1か所に絞る
- ログイン中の画面にはオブジェクトキャッシュ(Redis)が効く
- Node.jsはヘッドレス構成のフロントエンドとして使う場合に意味があり、通常のサイトの前段キャッシュには向かない