2026年5月26日

2026年5月26日

WordPressの急なアクセス集中に備える|ページキャッシュ・CDN・PHP-FPM・DB接続数の調整

SNS で記事が広まったり、テレビやニュースサイトで紹介されたりすると、WordPress に短時間でアクセスが集中します。WordPress は通常、ページを表示するたびに PHP を実行し、データベースに問い合わせます。そのため、アクセスが増えると PHP の処理数かデータベースの接続数が先に上限に達し、エラーや表示の遅れが起きます。

対策の中心は、PHP とデータベースまで届くリクエストを減らすことです。この記事では、詰まっている場所の見分け方、ページキャッシュと CDN の設定、PHP-FPM と MySQL の上限の合わせ方、緊急時にメンテナンス表示へ切り替える方法を、効果の大きい順に説明します。コマンドやパスは2026年10月時点の Ubuntu 24.04(Nginx・PHP 8.3)を例にしています。

どこが詰まっているかを確認する

見えている症状 確認する場所 主な原因
502 Bad Gateway や 504 Gateway Timeout が増える Nginx の /var/log/nginx/error.log PHP-FPM の処理数が足りない、処理が遅い
PHP-FPM のログに server reached pm.max_children setting と出る /var/log/php8.3-fpm.log PHP-FPM のワーカー数が上限に達している
「データベース接続確立エラー」が出る、ログに Too many connections MySQL のエラーログ MySQL の接続数が max_connections に達している
サーバー全体が重く、コマンドも遅い top・free -m CPU かメモリーが足りない(スワップが増えている)
# 直近のエラーを確認
sudo tail -n 50 /var/log/nginx/error.log
sudo grep -i "max_children" /var/log/php8.3-fpm.log | tail -n 5

# CPU・メモリーの状況
top
free -m

ワーカー数や接続数を増やす前に、次のページキャッシュと CDN で PHP まで届くリクエストを減らすほうが効果は大きくなります。

1. ページキャッシュを有効にする

ページキャッシュは、一度作った HTML を保存しておき、次からは PHP やデータベースを使わずに返す仕組みです。ログインしていない読者が同じ記事を見に来るアクセス集中には、最も効果があります。

ここではプラグインの WP Super Cache を例にします。「プラグイン」→「新規プラグインを追加」で WP Super Cache をインストールして有効化し、「設定」→「WP Super Cache」の画面でキャッシュを有効(Caching On)にして保存します。

WP Super Cache のキャッシュの配信方法は次のとおりです(2026年10月時点の公式プラグインページの説明による)。

方式 内容
Simple(シンプル) 保存した HTML を PHP が読み出して返す。.htaccess の変更が要らず、公式が推奨する方式
Expert(エキスパート) Apache の mod_rewrite で保存した HTML を直接返し、PHP を通らない。.htaccess の変更が必要

Nginx では .htaccess が使えないため、Simple 方式を使います。ログインしているユーザーやコメントを書いたユーザーには、保存した静的な HTML は返されません。

キャッシュが効いているかは、ログアウトした状態(またはシークレットウィンドウ)でページを2回読み込み、ページのソースの最後にある Cached page generated by WP-Super-Cache on … の日時が変わらないことで確認できます。Simple 方式では、応答ヘッダーにも WP-Super-Cache が付きます。

curl -sI https://example.com/ | grep -i wp-super-cache

WooCommerce のカート・決済・マイアカウントのように、人ごとに内容が変わるページはキャッシュしてはいけません。WP Super Cache の詳細設定で、キャッシュしない URL として指定します。

詳しい設定はWP Super Cacheの設定方法を参照してください。

2. CDNでHTMLもキャッシュする(Cloudflareの例)

Cloudflare を経由していると、画像・CSS・JavaScript などは既定でエッジ(Cloudflare のサーバー)にキャッシュされます。一方、HTML は既定ではキャッシュされません。アクセス集中のときに最も負荷になるのは HTML の生成なので、ログインしていない読者向けの HTML もキャッシュさせると、サーバーに届くリクエストを大きく減らせます。

Cloudflare のダッシュボードの Cache Rules(キャッシュルール)で、次の条件のルールを作ります。式は Edit expression(式の編集)の画面に直接書けます。

(http.host eq "example.com"
 and not http.request.uri.path contains "/wp-admin"
 and http.request.uri.path ne "/wp-login.php"
 and not http.request.uri.path contains "/wp-json/"
 and not http.cookie contains "wordpress_logged_in_"
 and not http.cookie contains "wp-postpass_"
 and not http.cookie contains "comment_author_")
  • キャッシュの適格性: Eligible for cache(キャッシュ対象にする)
  • Edge TTL: 「Ignore cache-control header and use this TTL」を選び、10分程度の短い時間にする

ログイン中のユーザーは wordpress_logged_in_ で始まる Cookie を持つため、この式でキャッシュの対象から外れます。WooCommerce を使っている場合は、woocommerce_items_in_cart の Cookie と、カート・決済・マイアカウントのページのパスも除外に加えます。

確認は応答ヘッダーの cf-cache-status で行います。2回目以降の読み込みで HIT になればエッジから返っています。DYNAMIC はキャッシュの対象外を意味します。

curl -sI https://example.com/ | grep -i cf-cache-status

注意点は次のとおりです。

  • オリジン(自分のサーバー)の応答に Set-Cookie があると、Cloudflare はキャッシュしません。プラグインがすべてのページで Cookie を発行していると HTML はキャッシュされないので、ヘッダーを確認してください
  • 記事を更新しても、Edge TTL の間は古いページが表示されます。急ぎで直したときは、Cloudflare のダッシュボードからキャッシュを削除(Purge)します
  • API でキャッシュルールを設定する場合、PUT https://api.cloudflare.com/client/v4/zones/{zone_id}/rulesets/phases/http_request_cache_settings/entrypoint はゾーンのキャッシュルールを丸ごと置き換えます。既存のルールを一覧で取得し、合わせて送らないと消えます

Cloudflare の導入手順はCloudflare CDNの設定方法を参照してください。

3. PHP-FPMのワーカー数をメモリーに合わせる

キャッシュから外れたリクエスト(ログイン中の操作、検索、フォーム送信など)は PHP-FPM が処理します。同時に処理できる数の上限が pm.max_children です。上限に達すると、後のリクエストは待たされ、Nginx が 502 や 504 を返します。

pm.max_children は「PHP-FPM に使えるメモリー ÷ 1ワーカーのメモリー使用量」を目安に決めます。メモリーを超える値にするとスワップが起き、かえって遅くなります。1ワーカーの平均の使用量は次のコマンドで確認できます。

ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%.0f MB (%d processes)\n", s/n/1024, n}'

たとえば PHP-FPM に 4GB 使えて、1ワーカーが平均 80MB なら、pm.max_children は 50 前後が目安です。

; /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500
  • pm.max_requests は、1つのワーカーが指定した数のリクエストを処理したら作り直す設定で、メモリーが少しずつ増えるのを抑えます
  • pm.process_idle_timeout は pm = ondemand のときだけ使われる設定で、pm = dynamic では効きません
sudo php-fpm8.3 -t && sudo systemctl reload php8.3-fpm

4. MySQLの接続数とPHP-FPMを合わせる

WordPress は、1回のページ表示の中でデータベースに1本の接続を使うのが基本です。そのため、同時に必要になる接続の数は、おおむね PHP-FPM のワーカー数の合計です。複数のプールや複数の Web サーバーがあれば、その合計で考えます。

MySQL の max_connections(MySQL・MariaDB とも既定値は 151)が、この合計に WP-Cron・WP-CLI・バックアップなどの分を足した数より大きいかを確認します。

SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';

Max_used_connections が max_connections に近い場合は、max_connections を上げるか、PHP-FPM のワーカー数を見直します。max_connections を上げると MySQL のメモリー使用量も増えるため、空きメモリーを確認してから変えてください。

# /etc/mysql/mysql.conf.d/mysqld.cnf の [mysqld] セクション
max_connections = 200

複数の Web サーバーから1台のデータベースに接続する構成では、ProxySQL のような接続プールのソフトを間に置く方法もあります。その場合も、ProxySQL からデータベースへの接続の上限は、データベース側の max_connections より小さくする必要があります。

接続数の調整についてはMySQLのmax_connectionsの設定も参照してください。

5. 緊急時にメンテナンス表示へ切り替える

それでもサーバーが応答しなくなったときは、管理者以外に 503 Service Unavailable を返し、サーバーを立て直す時間を作ります。次のコードを wp-content/mu-plugins/emergency-maintenance.php として保存しておくと、wp-content/emergency-maintenance.flag というファイルがあるときだけメンテナンス表示になります。

<?php
/**
 * Plugin Name: 緊急メンテナンス表示
 * Description: wp-content/emergency-maintenance.flag があるとき、管理者以外に 503 を返す。
 */

add_action( 'init', function () {
	if ( ! file_exists( WP_CONTENT_DIR . '/emergency-maintenance.flag' ) ) {
		return;
	}

	// WP-Cron と WP-CLI は止めない
	if ( wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
		return;
	}

	// 管理者がログインできるようにログイン画面は通す
	if ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
		return;
	}

	// ログイン済みの管理者は通す
	if ( current_user_can( 'manage_options' ) ) {
		return;
	}

	status_header( 503 );
	header( 'Retry-After: 600' );
	header( 'Content-Type: text/html; charset=UTF-8' );
	nocache_headers();
	echo '<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><title>メンテナンス中</title></head>';
	echo '<body><h1>ただいまアクセスが集中しています</h1><p>しばらくしてから再度お試しください。</p></body></html>';
	exit;
} );
# メンテナンス表示にする
sudo -u www-data touch /var/www/html/wp-content/emergency-maintenance.flag

# 元に戻す
sudo rm /var/www/html/wp-content/emergency-maintenance.flag

このコードは WordPress の読み込みの途中で 503 を返すため、データベースを使う処理の大半は省けますが、PHP は動きます。ページキャッシュや Cloudflare でキャッシュ済みのページは、そのままキャッシュから返されます。

6. Nginxの同時接続数を確認する

Nginx は少ないプロセスで多くの接続を扱えますが、同時接続の上限は worker_processes × worker_connections です。既定値が小さい場合は増やします。

# /etc/nginx/nginx.conf
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
}
sudo nginx -t && sudo systemctl reload nginx

事前にやっておくこと

  • ページキャッシュと CDN の HTML キャッシュは、アクセスが集中してからではなく、普段から有効にしておく
  • 負荷試験ツールで、キャッシュあり・なしでどのくらいのアクセスに耐えられるかを測っておく(本番ではなく検証環境で行う)
  • 画像は適切な大きさに縮小し、CDN から配信する
  • 複数台の Web サーバーで負荷を分ける構成にする場合は、アップロードしたファイルを共有ストレージやオブジェクトストレージに置き、サーバー間で内容が食い違わないようにする

まとめ

  • アクセス集中で先に詰まるのは、PHP-FPM のワーカー数か MySQL の接続数。ログで場所を確かめる
  • 最も効果があるのはページキャッシュ。さらに Cloudflare の Cache Rules でログインしていない読者向けの HTML をキャッシュする(HTML は既定ではキャッシュされない)
  • pm.max_children はメモリーから計算し、その合計が MySQL の max_connections に収まるようにする
  • pm.process_idle_timeout は pm = ondemand 専用
  • 緊急時は、管理者以外に 503 を返す仕組みを用意しておく

関連記事:

お気軽にご相談ください

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