wp-cronを無効化してsystem cronに移行する方法
WP-Cronはページ訪問時にしか実行されず、過疎サイトでは停止し、混雑時には処理が集中します。wp-cronを無効化し、WP-CLIとsystem cronで定期実行する設定と確認方法を解説します。
WP-Cron とは何か、なぜ system cron で置き換えるのか
WP-Cron は WordPress に組み込まれたタスクスケジューラーで、誰かがページをリクエストしたときにだけ実行されます。WordPress 内部で自動的に起動する処理はありません。キャッシュから配信されないリクエストのたびに、WordPress はスケジュールされたジョブの一覧を読み取り、実行時刻になったジョブがあれば、処理を行うため /wp-cron.php に自身への 2 回目の HTTP リクエストを送信します。このジョブを system cron に移行すると、その時刻にサイトへの訪問者が 1000 人いても 0 人でも、固定スケジュールで予測可能な実行を 1 回行えます。
実際に処理を行うのは、wp-config.php 内の定数と crontab エントリの 2 行です。このガイドの残りの部分では、その 2 行だけでは分からない点を扱います。ジョブを実行すべきユーザー、スケジュールされたイベントが実際に実行されたことを確認する方法、そしてサイト上に何も表示せずに設定が失敗する 3 つのパターンについて説明します。
例では、WordPress のディレクトリとして /srv/www/example.com、Web サーバーのユーザーとして www-data を使用します。すべての箇所で、自身のパスとユーザーに置き換えてください。
混雑したサイトで、どの訪問者が cron のコストを発生させたのか
キャッシュされていないリクエストでは、毎回チェックのコストが発生します。WordPress は cron オプションを読み込み、タイムスタンプを比較します。実行対象がある場合は spawn_cron() を呼び出し、/wp-cron.php へのノンブロッキングなループバックリクエストを送信します。訪問者は結果を待ちません。しかし、PHP ワーカーは待機します。pm.max_children = 5 を使用する PHP-FPM が動作する小規模な VPS では、遅いスケジュール済みジョブが完了するまで、PHP の処理能力の 5 分の 1 を占有します。しかも、ページの読み込みが最も多い最繁忙時に実行される可能性が高くなります。
WordPress は重複実行を制限しています。存続期間が WP_CRON_LOCK_TIMEOUT のロックを取得します。デフォルトは 60 秒です。そのため、同時にアクセスした訪問者がそれぞれ処理を開始することはありません。ロックによって重複実行は抑えられます。しかし、処理をリクエスト処理の経路から外すことはできません。
影響を判断する前に、自分のサーバーで実行頻度を確認してください。各ループバックリクエストは Web サーバーのアクセスログに記録されます。
sudo grep -c 'wp-cron.php' /var/log/nginx/access.logApache の場合は、代わりに /var/log/apache2/access.log に記録されます。1 日あたり数千回に達するなら、実際のコストが発生しています。他の変更を行う前後で VPS のベンチマークを実施するのと同じように、記事の数字を参照するのではなく、自分のサーバーで測定すべき種類の値です。
キャッシュを使用すると状況が変わります。ページキャッシュが大半のリクエストに静的 HTML を返す場合、それらのリクエストでは PHP が実行されません。そのため、cron のチェックも発生しません。キャッシュを十分に利用している混雑したサイトは、次に示す静かなサイトに近い動作になります。
訪問者が少ないサイトで cron が実行されない原因
訪問者がいなければ、cron も実行されません。1 日に数回しかアクセスされないサイトでは、スケジュールされたジョブも、訪問者がアクセスした不定期のタイミングで 1 日に数回しか実行されません。
症状はどれも同じです。09:00 に公開予定の投稿が、誰かがページを読み込むまで投稿一覧で Missed schedule のままになります。バックアッププラグインは夜間の処理を実行できません。更新チェックも遅れるため、セキュリティリリースがすでに公開されていても、ダッシュボードには更新対象が表示されません。注文メール、更新通知、期限切れ警告も遅れて送信されます。
この問題では、エラーがログに記録されません。WordPress から見ると、ジョブは遅延していません。そもそも開始されていないためです。
Step 1: wp-config.php で訪問者トリガーを無効にする
/srv/www/example.com/wp-config.php を開き、次の定数を追加します。
define( 'DISABLE_WP_CRON', true );/* That's all, stop editing! Happy publishing. */ と記載された行より上に置いてください。その直下のコメントの次の行では wp-settings.php が必要であり、wp-settings.php で WordPress が cron のチェックを init にフックするためです。その require の後で定数を定義すると設定が間に合わず、ファイル上は正しく見えてもトリガーが動き続けます。
想定した位置に行があることを確認します。
grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.phpこの定数は、イベントのスケジュール登録を停止しません。プラグインはこれまでどおりキューにジョブを追加し続けます。停止するのは、ページの読み込みによってそのキューが実行されることだけです。そのため、step 3 を完了するまでキューは実行されません。
/wp-cron.php への直接リクエストもブロックしません。誰でもその URL をリクエストできますが、通常は問題ありません。このファイルは期限が来た処理だけを実行するためです。Web サーバーの設定でブロックするかどうかは任意です。ただしブロックすると、このガイドの後半にある curl のフォールバックも動作しなくなります。
手順 2: WP-CLI をインストールする
WP-CLI は WordPress 用の公式コマンドラインツールです。Web サーバーの PHP モジュールとは別パッケージである、PHP のコマンドラインバイナリが必要です。
php -v
sudo apt install -y php-cli公式のインストールガイドが推奨している phar ビルドから WP-CLI をインストールします。
cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --infophp wp-cli.phar --info は、PHP バイナリのパス、PHP のバージョン、WP-CLI のバージョンを表示します。3 つすべてが表示されれば、phar は動作しています。2026 年 8 月時点で、インストールガイドに記載された PHP の最低要件は 7.2.24 です。Ubuntu 24.04 には PHP 8.3 が含まれるため、現在のサーバーはこの要件を十分に満たしています。後で sudo wp cli update により更新できます。
WP-CLI はサイトのユーザーとして実行し、root では実行しないでください。
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core versionroot として実行すると、WP-CLI は起動を拒否します。
Error: YIKES! It looks like you're running this as root.--allow-root が提案されます。ここでは使用しないでください。理由は、以下の最初の失敗パターンで説明します。
また、sudo -u www-data -i は動作しません。このアカウントのログインシェルが /usr/sbin/nologin であり、This account is currently not available. が表示されるためです。コマンドを sudo -u に直接渡すと、ログインシェルを経由しないため、正常に実行されます。
次に、WordPress 自体が手順 1 で設定した定数を認識していることを確認します。
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'bool(true) が表示されます。未定義の定数に関する致命的なエラーが表示された場合、define() の行まで処理が到達していません。通常は、その行が require より下に置かれています。
Step 3: cron エントリを適切なユーザーとして追加する
適切なユーザーは、PHP が書き込むファイルを所有しているユーザーです。両端を確認します。
stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.confデフォルトの Ubuntu インストールでは、どちらも www-data になります。サイト専用の PHP-FPM pool と専用ユーザーを設定している場合は、以下のすべてでそのユーザーを使用します。Ubuntu 24.04 の LAMP stack でサイト単位の構成にすると、通常はこの形になります。
そのユーザーが書き込めるログディレクトリを作成します。
sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cronそのユーザーの crontab を編集します。
sudo crontab -u www-data -e次の1行を追加します。
*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1各部分の意味は次のとおりです。*/5 は5分ごとに実行します。flock -n はロックファイルを取得し、前回の実行がまだロックを保持している場合は直ちに終了します。/usr/local/bin/wp は絶対パスです。cron には絶対パスが必要です。--path により、任意の作業ディレクトリからコマンドを実行できます。--due-now はキュー内のすべてのイベントではなく、実行時刻に達したイベントだけを実行します。リダイレクトにより、標準出力とエラーが1つのファイルに書き込まれます。
実運用では、このリダイレクトは必須です。cron はジョブの出力をそのユーザー宛てにメールで送信しますが、ほとんどの VPS イメージにはメール転送エージェントがインストールされていません。その場合、cron は (CRON) info (No MTA installed, discarding output) をログに記録して、出力を破棄します。ファイルに保存すれば、実行結果を確認できます。
ファイルが保存されたことを確認します。
sudo crontab -u www-data -l複数のサイトがある場合は、同時に実行されないよう、分単位をずらしてサイトごとに1行ずつ設定します。
*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1ログはローテーションしない限り増え続けます。/etc/logrotate.d/wp-cron を記述します。
/var/log/wp-cron/*.log {
weekly
rotate 4
missingok
notifempty
compress
create 640 www-data www-data
}何も変更せずに構文を確認します。sudo logrotate --debug /etc/logrotate.d/wp-cron。
手順 4: スケジュールされたイベントが実際に実行されたことを確認する
crontab の行を正常に保存できても、それだけでは何も証明できません。まず、最も簡単な確認から始め、実行結果を確定できる確認まで進めます。
最初に、cron はコマンドを起動したでしょうか。cron は専用の unit を使って journal にログを記録します。
journalctl -u cron.service --since "15 min ago" | grep wp正常なエントリは次のようになります。先頭のタイムスタンプとホスト名は省略しています。
CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)この行は、cron が www-data としてコマンドを起動したことを示します。コマンドが正常に実行されたかどうかは示しません。
次に、WordPress は何らかの処理を実行したでしょうか。ログファイルを読み取ります。
sudo tail -n 20 /var/log/wp-cron/example.logWP-CLI はイベントごとに1行を出力し、最後に合計を出力します。
Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.エラーも同じファイルに記録されます。これが 2>&1 の目的です。ほとんどの実行では処理対象がなく、出力も少量です。そのため、処理待ちのイベントがあると分かっている実行の後にファイルを読み取ってください。
最後に、最初から最後まで実行されたことを確認します。マーカー用のイベントを登録し、一覧から消えることを確認します。
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_check1回分の間隔を待ってから、一覧コマンドを再度実行します。1回限りのイベントは実行時にキューから削除されるため、hook は一覧から消えます。この hook 名に対する callback を登録する plugin はないため、実行してもサイト上で他の処理は発生しません。2回分の間隔を待っても hook が一覧に残っている場合、キューが実行されていません。最初の2つの確認により、問題が cron にあるのか WP-CLI にあるのかを判断できます。
この確認には wp cron test を使用しないでください。このコマンドは、訪問者による起動処理が機能するかどうかを確認するものです。また、DISABLE_WP_CRON が true の場合はエラーになります。正しく設定されたサーバーでは、そのエラーが想定される出力であり、障害ではありません。
systemd タイマーを使う方法
サーバー上の定期処理を systemd のサービスとタイマーですでに実行している場合は、WordPress も同じ仕組みに組み込みます。実行のたびに systemctl list-timers に記録され、ローテーションが必要なファイルではなく journal に出力されます。
/etc/systemd/system/wp-cron-example.service を作成します。
[Unit]
Description=Run due WordPress cron events for example.com
[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now続いて /etc/systemd/system/wp-cron-example.timer を作成します。
[Unit]
Description=Run WordPress cron for example.com every 5 minutes
[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20systemd は同じサービスを同時に 2 つ実行しないため、この方法では flock は必要ありません。Persistent=true を指定すると、マシンの停止中に実行されなかった処理を、起動後に実行できます。crontab のエントリでは、この処理はできません。
crontab またはタイマーのどちらかを選択してください。同じサイトに対して両方を実行すると、キューが 2 回処理されます。メールや注文処理が重複して実行されると、顧客にも分かります。
root として cron ジョブを実行してはいけない理由
これは、設定が失敗する3つのパターンの1つです。root の crontab にジョブを登録すると、WP-CLI は何も実行しないまま終了します。
Error: YIKES! It looks like you're running this as root.キューは実行されません。出力をリダイレクトしていなければ、このメッセージも確認できません。危険な対処は --allow-root を追加することです。これを行うと、その実行中にプラグインが作成するすべてのファイルの所有者が root になります。次の Web リクエストは www-data として実行されるため、それらのディレクトリに書き込めません。その結果、サイトには次のようなエラーが表示されます。
Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?所有権を修復してから、ジョブを移動します。
sudo chown -R www-data:www-data /srv/www/example.com/wp-contentroot の crontab と www-data の crontab は別々のファイルです。一方から行を削除しても、もう一方には影響しません。両方を確認します。
sudo crontab -u root -l
sudo crontab -u www-data -lcron が wp: not found を報告する理由
これは 2 つ目の失敗です。cron はユーザージョブに非常に短い PATH を設定します。/usr/bin:/bin。WP-CLI は /usr/local/bin にインストールされますが、このパスは一覧に含まれていません。ジョブは開始直後に失敗し、ログには次の 1 行だけが残ります。
/bin/sh: 1: wp: not found推測せず、cron の環境を確認してください。一時的に次の行を追加します。
*/5 * * * * env > /tmp/cron-env.txt 2>&11 回の実行間隔が過ぎたら /tmp/cron-env.txt を読み、その行を削除します。このファイルの PATH= の値が、ジョブに実際に渡される値です。
修正方法は 2 つあります。手順 3 と同じように、絶対パス /usr/local/bin/wp を使用します。または、すべてのジョブ行より上にある crontab の先頭で PATH を 1 回設定します。
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin同じ問題は、さらに 1 段階下にもあります。wp phar は #!/usr/bin/env php で始まるため、シェルから php も見つけられる必要があります。PHP が /usr/bin の外部にある場合、カスタムビルドやコントロールパネルのビルドではこの構成が発生します。その場合、次のエラーになります。
/usr/bin/env: 'php': No such file or directoryその場合は、たとえば /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now のようにインタープリターを明示的に指定してください。
1 分間隔で元の問題が再発する理由
今回が 3 回目の失敗です。* * * * * は 5 分より安全に感じますが、負荷の高いサイトでは開始時点の状態に戻ってしまいます。1 回の実行に間隔以上の時間がかかると、1 回目がまだ実行中のまま、次の実行が始まります。10 分後には、各プロセスがそれぞれメモリとデータベース接続を保持する PHP プロセスが 10 個存在する状態になります。
実際に実行の滞留を確認します。
ps -eo etimes,user,args | grep '[c]ron event run'etimes はプロセスの経過時間を秒で示します。1 行だけなら正常です。間隔を大幅に超える経過時間の行が複数ある場合、実行が重なっています。小規模な VPS では、最終的に MySQL の Too many connections エラーが発生するか、カーネルがメモリを回収するために PHP を終了させます。この状況は sudo dmesg -T | grep -i 'killed process' で確認できます。
WP-CLI は wp-cron.php にリクエストを送るのではなく、イベントのコールバックを直接実行します。そのため、WordPress が重複起動を防ぐために使用する 60 秒のロックは適用されません。step 3 のエントリにある flock -n が、現在は重複実行を防ぎます。スキップされた実行は、設計上、直ちに終了し、何も出力しません。重複実行は crontab で解決する必要があります。ホストのカーネルが自動的に解決する問題ではありません。Linux kernel 7.2 で追加されたキャッシュ対応のタスク配置でさえ、プロセスを配置するコアを決めるだけで、起動したプロセスの数を制御することはありません。
間隔は、実際に依存している最短のスケジュールを基準に決めます。まず 1 回の実行時間を測定してください。
time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now5 分は妥当なデフォルトです。09:00 に予約した投稿は 09:05 までに公開されます。時間依存の処理がないサイトなら、15 分でも問題ありません。1 分間隔は、実際にその頻度を必要とする店舗やキュー駆動のプラグインに限ります。また、1 回の実行が数秒で完了することを確認してから使用してください。
WP-CLI をインストールできない場合
一部のホストでは、シェルツールが制限されています。wp-cron.php への通常の HTTP リクエストでも、Web スタック全体を経由して同じキューを実行できます。
*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/null失われる点は明確です。
- 実行時間は Web サーバーと PHP-FPM のリクエストタイムアウトによって制限されるため、長時間のジョブは途中で打ち切られることがあります。
- 証明書が有効でないと
curlはSSL certificate problemで停止します。そのため、nginx で Certbot を使用する 構成で更新を継続できるようにしてください。 - ページキャッシュで
wp-cron.phpをキャッシュしないでください。キャッシュされたレスポンスが cron リクエストに返され、何も実行されなくなります。 - イベントごとの出力は得られません。そのため、ジョブが実行されたことを示す唯一の証拠は、ジョブによって生じた結果です。
-sS は成功時に curl の出力を抑制しつつ、エラーは表示します。cron ジョブではこの動作が適しています。
サーバーのスケジュールにはほかに何を含めるか
WordPress のキューを system cron で管理するようにしたら、サーバー上の定期処理も同じ場所で管理し、確認できる状態にします。オペレーティングシステムのセキュリティパッチは、手動で管理する cron の行ではなく、unattended upgrades に任せます。WordPress のプラグインとテーマの更新は、別途判断が必要です。crontab に wp plugin update --all を設定すると、監視する人がいない深夜 3 時に稼働中のサイトを簡単に壊す可能性があります。更新は意図的に実行するか、ステージング環境での確認とバックアップを経て実行します。
FAQ
WP-Cron を無効にすると、予約投稿の公開は停止しますか?
いいえ、別の仕組みがキューを実行する限り停止しません。DISABLE_WP_CRON は、ページの読み込みによってキューが実行されることだけを停止します。イベントはこれまでどおり予約されます。09:00 に設定した投稿は、09:00 後に最初に cron が実行された時点で公開されるため、5 分間隔なら 09:05 までに公開されます。定数を設定しただけで cron エントリを追加しない場合、投稿は Missed schedule と表示されたまま一覧に残り、何かがキューを実行するまで公開されません。
WordPress の cron ジョブは、どのユーザーで実行すべきですか?
PHP が書き込むファイルの所有者で実行します。デフォルトの Ubuntu インストールでは www-data です。stat -c '%U %G' /srv/www/example.com/wp-content/uploads で確認し、PHP-FPM の pool 設定にある user = の行と比較してください。root でジョブを実行すると、WP-CLI は YIKES エラーで停止します。--allow-root で強制的に実行すると、wp-content 内に root 所有のファイルが作成され、後で Web サーバーが書き込めなくなります。
system cron で WordPress の cron はどの頻度で実行すべきですか?
ほとんどのサイトでは 5 分ごとで十分です。実際に必要な最短のスケジュールに間隔を合わせ、1 回の実行にかかる時間を十分に上回る間隔にしてください。実行時間は、WP-CLI コマンドの前に time を付けて測定できます。負荷の高いサイトでは、flock が実行を保護しない限り、1 分間隔にすると実行が重複します。
WP-Cron を無効にすると、wp cron test はなぜ失敗しますか?
このコマンドは、訪問者のアクセスを契機とする実行をテストするためです。DISABLE_WP_CRON が true に設定されていると、エラーを報告します。この構成のサーバーでは、これは正しい結果です。代わりに system cron の経路を確認してください。/var/log/wp-cron/example.log を読み取るか、wp cron event schedule でマーカーイベントを予約し、次回の実行後に wp cron event list からそのイベントが消えていることを確認します。
WP-CLI は必要ですか、それとも wp-cron.php への curl で十分ですか?
curl で動作します。WP-CLI をインストールできない場合は、curl を使用するのが適切です。ただし、Web サーバー経由で WordPress を読み込むため遅く、リクエストのタイムアウトの影響も受けます。WP-CLI は Web のタイムアウトなしでコマンドラインの PHP プロセスからイベントを実行します。イベントごとに所要時間付きで 1 行を出力するため、ログから何が実行され、どれだけ時間がかかったかを正確に確認できます。