WP-Cronを無効化してsystem 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() を呼び出します。spawn_cron() は、/wp-cron.php へノンブロッキングのループバックリクエストを送信します。訪問者は結果を待ちません。PHP ワーカーは待ちます。pm.max_children = 5 を使用する PHP-FPM が小規模な VPS で稼働している場合、1 件の遅いスケジュール済みジョブが、処理にかかる時間だけ 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 から見れば、ジョブは遅れていません。そもそも開始されていないためです。
手順 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この定数は、イベントのスケジュール登録を停止しません。プラグインはこれまでどおりキューにジョブを追加し続けます。停止するのは、ページの読み込みによるキューの実行だけです。そのため、手順 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 の下に記述されていることが原因です。
ステップ 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 プールを設定している場合は、そのユーザーを以下のすべての操作で使用します。これは、Ubuntu 24.04 で LAMP スタックを構築するサイト単位の構成で通常採用される方法です。
そのユーザーが書き込めるログディレクトリを作成します。
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 回限りのイベントは実行時にキューから削除されるため、フックは表示されなくなります。どのプラグインもそのフック名にコールバックを登録していないため、実行してもサイトにほかの処理は発生しません。2 回分の間隔を過ぎてもフックが表示される場合、キューが実行されていません。最初の 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 段階下でも発生します。phar の wp は #!/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 です。スキップされた実行は、設計上、直ちに終了し、何も出力しません。
間隔は、実際に依存しているスケジュールのうち最短のものを基準に選びます。まず実行時間を測定してください。
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 分間隔は、本当に必要とするストアやキュー駆動のプラグインに限って使用し、かつ実行が数秒で完了することを確認してから設定してください。
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 ジョブに適した動作です。
サーバーのスケジュールに追加すべき処理
system cron で WordPress のキューを管理するようにしたら、サーバー上の定期処理も同じ場所に集約し、確認できる状態にしておきます。オペレーティングシステムのセキュリティパッチは、手動で管理する 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 を付けて測定できます。負荷の高いサイトで 1 分間隔にすると、flock が実行を制御しない限り、実行が重なることがあります。
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 行を出力するため、ログから実行された処理と所要時間を正確に確認できます。