09:00に予約した記事が、昼に自分でサイトを開いた途端に公開された。そんな遅れ方なら、まずWP-Cronの起動経路を確認します。通常のWP-Cronは、ページへのアクセスをきっかけに期限の来た処理を動かす仕組みです。アクセスがなければ、予定時刻を過ぎても処理が始まらないことがあります。
サーバーのcronから定期的に起動すれば、アクセス待ちを避けられます。ただし、先に代わりの起動方法を試し、その後で通常の起動を止める順番です。5分ごとのcronなら次の実行まで待つ時間があり、処理時間や障害も加わります。「09:00:00ちょうど」を保証する設定ではありません。
最初に、イベントがあるのか、実行が遅いのかを分ける
管理画面の「設定 → 一般」でサイトのタイムゾーンを確認し、問題の投稿が予約済みになっているかを見ます。日時の入力違いや、予約イベント自体がない場合は、起動回数だけ増やしても解決しません。WP-CLIを利用できる環境では、対象サイトを絶対パスで指定して次を確認します。
wp --path=/home/example/public_html option get timezone_string
wp --path=/home/example/public_html cron event list --fields=hook,next_run_gmt,next_run_relative
wp --path=/home/example/public_html cron test
next_run_gmtはGMT/UTCの予定日時です。日本時間の09:00と比べるなら、UTCでは同日の00:00になります。timezone_stringが空なら、固定オフセットの設定も管理画面で確認してください。期限を過ぎたイベントが残っているのか、そもそも登録されていないのかを分けて記録します。
wp cron testはHTTPによる通常の起動経路を試すコマンドで、読み取りだけの一覧表示ではありません。実際に期限の来たイベントが動き得るため、公開やメール送信などの影響を把握してから実行します。失敗した場合は、HTTP応答、サイトヘルス、PHPエラーを調べます。ホスト側ですでにcronを代行している場合は、重ねて登録せず、その実行記録を確認してください。
予約時刻がずれたら、日時・イベント・実際の状態を読む
WordPressのタイムゾーン、記事に登録された現地時刻とGMT、publish_future_postの予定を照合します。例えば日本時間9時とUTCの0時は、同じ時点を表します。数字だけを見て、別の予約が登録されたと判断しないでください。
予定どおりのイベントがあるのに遅れた場合は、cronをいつ起動したかとログを確認します。記事の日時自体が違うなら、登録時の変換を先に調べます。n8nの起動時刻とWordPress側の公開予定は、別の項目として記録します。
WP-CLIかcurlかを、使える環境と確認方法で選ぶ
| 方法 | 向く環境 | 確認できることと限界 |
|---|---|---|
WP-CLIの cron event run --due-now |
サーバー上で対応PHPとWP-CLIを実行できる | 期限到来のhookを実行し、コマンドの終了と出力を記録できる。プラグイン内の業務処理が成功したかは別に確認 |
curlで wp-cron.php を呼ぶ |
HTTP呼び出しが許可され、対象URLへ到達できる | 接続・HTTP応答を記録できる。200でも全イベントの完了や成功までは分からない |
CLIではWeb側とPHPのバージョン・拡張・実行ユーザーが異なることがあります。cronに使うユーザーで command -v php wp curl flock と php -v を確認し、WP-CLIで対象サイトを読み込めるか試します。以下の /home/example/ とコマンドの絶対パスは置き換え用です。サイトの通常の管理ユーザーで実行し、権限エラーをroot実行で押し通さないようにします。
CLI例の --due-now は期限の来たイベントを実行します。--all に変えると未来のイベントまで対象になるので、定期処理には置き換えません。プラグインを一括スキップすると必要なコールバックが読み込まれないことがあるため、安易に --skip-plugins を足すのも避けます。
起動用スクリプトを試してから、通常のWP-Cronを止める
設定変更前にDB・wp-config.php・現在のcron設定をバックアップします。Linuxの単一サーバーで flock が使える場合は、次のスクリプトで前回処理との重なりと終了コードを記録できます。先に、実行ユーザーが書き込める非公開の /home/example/logs/ と、スクリプト用の /home/example/bin/ を用意してください。ログはWeb公開ディレクトリの外に置きます。
#!/bin/sh
# Linux single-server example. Replace paths after checking your hosting setup.
PATH=/usr/local/bin:/usr/bin:/bin
export PATH
stamp() { date -u '+%Y-%m-%dT%H:%M:%SZ'; }
exec 9>/home/example/logs/wp-cron-example.lock
if ! /usr/bin/flock -n 9; then
printf '%s lock_busy\n' "$(stamp)"
exit 75
fi
printf '%s start\n' "$(stamp)"
/usr/local/bin/wp --path=/home/example/public_html cron event run --due-now
result=$?
printf '%s end exit=%s\n' "$(stamp)" "$result"
exit "$result"
これを /home/example/bin/run-wp-cron.sh に保存します。パスを確認したうえで、cronに使うユーザーから /bin/sh /home/example/bin/run-wp-cron.sh を一度実行します。ここでは期限の来た投稿公開やバックアップも動き得ます。検証環境で試してから、本番の実行対象と結果を確認してください。
次に、そのユーザーの crontab -e で下の行を追加します。既存の行は残します。共用サーバーの管理画面に「分・時・日…」が分かれている場合は、時間欄とコマンド欄へ分けて登録し、サーバーが許可する最短間隔に合わせます。
*/5 * * * * /bin/sh /home/example/bin/run-wp-cron.sh >> /home/example/logs/wp-cron-example.log 2>&1
定期実行のログが出て、対象イベントが処理されることを確認したら、wp-config.php のWordPress読み込みより前に次を設定します。すでに同じ定数があれば、その値を変更し、重複定義しません。
define( 'DISABLE_WP_CRON', true );
DISABLE_WP_CRON は通常のアクセスによる自動起動を止めます。登録済みの予約イベントを削除する設定ではなく、WP-CLIや wp-cron.php を直接呼ぶ経路は使えます。切り替え後にも定期実行と結果を確認してください。代替経路が動かなければ、この設定を元に戻してアクセスによる起動を復旧し、追加したcron行を止めて原因を調べます。
curlを使う場合は、スクリプト内の実行行を入れ替える
WP-CLIの実行行を次に置き換え、直後の result=$? と終了ログは残します。WP-CLI版とcurl版を同じサイトで二重に定期登録する必要はありません。
/usr/bin/curl --fail --silent --show-error --connect-timeout 10 --max-time 60 \
--output /dev/null --write-out 'http=%{http_code}\n' \
'https://example.com/wp-cron.php'
URLは実際のWordPress設置先の wp-cron.php にします。サブディレクトリ設置にも注意してください。このコマンドはスクリプトの中へ保存します。%{http_code} を含む行をcrontabへ直接貼ると、cronの % の扱いによって意図と違う実行になる場合があります。
403ならWAFやアクセス制御、301/302ならURLの正規化先を確認します。認証ページやキャッシュされた応答の200を成功扱いしないため、イベントの実行結果も照合します。タイムアウトした場合も、サーバー側では処理が続いている可能性があります。直後に何度も手動再実行せず、PHPログと対象処理の状態を見て判断します。
ログ・予約イベント・実際の投稿状態を突き合わせる
移行後の wp cron test は、DISABLE_WP_CRON=true を理由にエラーになります。これは通常の起動を意図的に止めた結果であり、サーバーcronの動作確認には使えません。代わりに「定期起動された」「処理が終わった」「予定した結果になった」の3段階を見ます。
wp --path=/home/example/public_html cron event list --hook=publish_future_post --fields=hook,next_run_gmt,next_run_relative
wp --path=/home/example/public_html post get 123 --fields=ID,post_status,post_date,post_date_gmt --format=json
123 は確認対象の投稿IDへ置き換えます。予約イベントの一覧と投稿状態を確認し、予定を過ぎた対象が publish になったかを見ます。公開ページが古いままならキャッシュも確認します。投稿の post_date だけでは実際に公開へ変わった瞬間は分からないため、厳密な遅延計測には状態遷移のログが必要です。
| 記録項目 | 書き方の例 |
|---|---|
| 予定日時・タイムゾーン | 2026-10-01 09:02:00 JST |
| cron起動・終了 | 09:05:00開始、09:05:04終了、exit=0 |
| 実行結果を確認した時刻 | 09:05:10に投稿がpublishと確認 |
| 遅延の扱い | 状態遷移ログなしなら「09:05:10時点で公開済み」。実際の公開時刻・遅延秒数は未確定 |
表は記録方法の例です。5分周期では09:02の予定を09:05の起動で処理する、といった待ちが生じます。周期より大きな遅れが続く場合は、lock_busy の連続、長時間のバックアップ、PHPエラー、サーバー停止を調べます。exit=0でも外部送信やバックアップ生成まで成功したとは限らないので、各プラグインの結果も確認します。
複数サイトでは、間隔より先に実行先と負荷を分ける
独立したWordPressが複数ある場合は、サイトごとにパス・ログ・ロックファイルを分けます。同じロックを使うと別サイトの処理に邪魔されます。開始分をずらし、処理時間を見て間隔を調整してください。マルチサイトは --url による対象指定など構成に応じた設計が必要です。
上のファイルロックは、同じサーバー上で同じロックファイルを使う起動同士を抑えるものです。別サーバーのジョブまで一括で排他する仕組みではありません。また、flock がない共用サーバーではこのまま動かないため、ホストが用意する重複起動防止や推奨方法を確認します。ロック行だけを消して短い周期で回す前に、前回の処理が終わるかを確かめてください。
運用ではログの保存期間・ローテーションと、終了エラーや長時間未実行の通知先を決めます。短い周期へ変えることより、予定した処理が実際に終わっているかを追えることが大切です。原稿登録の設計はWordPressへAI原稿を下書き登録する実装、AIによる定期的な調査はClaude Code Routinesの保守例も参照できます。
仕様確認:2026年9月12日。隔離したWordPressでWP-CLIによる期限到来イベントの実行と、HTTPからの予約投稿公開を確認しています。OSの定期起動設定はサーバーごとに確認してください。