予約投稿の時間になっても公開されない。毎朝のバックアップが取られていない。定期メールが飛んでいない。設定を何度見直しても間違いは見つからないのに、動いていない。WordPressの定期実行でこの状態になったとき、原因はたいてい設定値ではなく「実行の引き金がどこにあるか」です。WP-Cronは時計ではなく、アクセスに便乗して動く仕組みだからです。ここでは、遅れて動いているのか一度も動いていないのかを分ける手順と、サーバー側のcronに寄せたあとで踏んだ落とし穴までを書きます。
WP-Cronは時計ではありません
WordPressの定期実行は、時刻になったら自動で起きる常駐プログラムではありません。誰かがサイトのページを開いたときに、「予定時刻を過ぎたまま実行されていないタスクはないか」を確認し、あれば裏側で wp-cron.php を呼び出して処理する、という作りになっています。
つまり、アクセスがなければ何も起きません。午前3時に設定したタスクは、次に誰かが訪れた午前9時に動きます。アクセスの少ないサイトほど遅れ、深夜のタスクほどずれます。「時間どおりに動かない」の大半はこれで説明がつきます。逆に言えば、時間どおりに動かないことと、一度も動いていないことは、原因がまったく違います。ここを分けずに調べ始めると遠回りになります。
遅延か未実行か。切り分けは3手で済みます
最初に、予定表そのものを見ます。スケジュールを一覧表示するプラグインを入れるか、WP-CLIが使えるなら wp cron event list で登録済みのイベントと次回実行時刻が出ます。
ここで予定時刻を過ぎたイベントが溜まっているなら、登録はできていて引き金だけが足りていません。アクセス不足か、後述する接続の遮断を疑います。反対に目的のイベントが一覧に無いなら、そもそも登録されていません。プラグインの有効化時にしかスケジュールを登録しない作りは多く、更新や再有効化のタイミングで取りこぼされることがあります。この場合は登録処理をやり直します。
3手目は、wp-cron.php をブラウザやコマンドで直接叩いてみることです。手で叩けば動くなら、処理そのものは正常で、呼ばれていないだけだと確定します。ここまでで原因の範囲は大きく絞れます。
原因と対処の対応表
| 症状 | 疑うところ | 対処 |
|---|---|---|
| 設定時刻より遅れて実行される | アクセス不足 | サーバーのcronから定期的に叩く |
| 深夜のタスクだけ動かない | 同上。夜間はアクセスがない | 同上 |
| まったく動かない | DISABLE_WP_CRON を true にしたまま、サーバー側のcronを設定していない | サーバーのcronを登録する |
| まったく動かない(管理画面は正常) | Basic認証・IP制限・WAFで自サイトへの内部接続が塞がれている | 認証の除外設定を入れるか、外部からcronで叩く |
| 途中まで動いて止まる | PHPの実行時間制限、メモリ上限 | 処理を分割する。制限値を見直す |
| 同じ処理が2回走る | WP-Cronとサーバーcronの併用、または重複登録 | 片方に寄せる。重複を防ぐ仕組みを入れる |
| 実行時刻が数時間ずれる | タイムゾーンの設定 | WordPress側の設定とサーバー側の時刻を確認する |
| サーバーcronにしたのに動かない | cronの登録内容、PHPの実行パス、後述の統合 | 実行ログを出して確認する |
サーバーのcronに寄せる場合の書き方
アクセス数に依存させたくないなら、WP-Cronを止めてサーバー側のcronから叩く形にします。wp-config.php に1行足します。
<code>define('DISABLE_WP_CRON', true);</code>そのうえで、サーバーの管理画面かcrontabに実行を登録します。
<code>/usr/bin/php /home/username/yoursite/public_html/wp-cron.php >/dev/null 2>&1</code>
この2つは必ずセットです。定数だけ書いてcronを登録しないと、定期実行が全部止まります。予約投稿もバックアップも通知も、静かに動かなくなります。移行や引っ越しの直後にこの状態が発生することがあり、原因が分からないまま数週間が過ぎる例を見ています。
実際に踏んだのは、サーバー側の仕様でした
同じサーバー契約の中で複数のサイトを運用している事業者から、スタッフの当日の稼働状況を外部ページから収集し、社内用のCSVとして落とせるようにする機能を作りました。収集はサーバーのcronで毎朝実行し、集めたデータを専用のテーブルに入れる仕組みです。処理量は約850件で6分弱でした。
この機能を、同じ事業者の2つめのサイトへ横展開したときに事故が起きました。新しいサイトの分は動いたのですが、すでに稼働していた1つめのサイトの収集が止まりました。しかも、こちらは完了報告を出した後で、数日たってから指摘を受けています。
原因は、そのサーバーのcron機能の挙動でした。実行時刻とコメント欄が同一のcronは、同じコマンドとして統合されてしまうのです。1つめのサイトと同じ時刻、同じコメントで2つめを登録した結果、先に登録されていたほうが上書きされる形になりました。公式のドキュメントに明記されている挙動ではないため、一度踏むと原因の特定に時間がかかります。
対処は単純で、実行時刻をずらす(5時30分と5時40分にする)か、コメント文言にサイト名を入れて一意にすることです。同じサーバーアカウントで複数サイトを運用している場合、この条件は簡単に成立します。以来、横展開の作業では「既存サイト側が今までどおり動いているか」を完了チェックに必ず入れています。テーマファイルが完全に独立していても、cron・データベース・PHPのバージョン・WAFの設定は共有されているからです。
この案件では、あと2つ紛らわしい症状が出ました。ひとつは「CSVのダウンロードボタンが反応しない」という報告で、実際にはボタンではなくデータが1件も入っていなかったのが理由です。当日分だけを集める仕様なので、導入した初日は必ず空になります。cronが未設定だったことと重なって、動いていないように見えていました。この種の報告を受けたら、画面側のコードより先にテーブルの中身を見るほうが早く着きます。
もうひとつは「一覧に2件ずつ重複して表示される」というものです。これも表示側の不具合ではなく、バッチが二重に実行されてデータが2回登録されていました。定期実行まわりの不具合は、症状が画面に出るぶん表示の問題に見えますが、原因はほぼデータ側にあります。日次の取り込み処理をどう組み立てるかは、毎日データを取り込んでサイトに表示する仕組みを、無理なく作るにまとめました。
再発させないための作り方
まず、実行ログを残します。開始時刻、終了時刻、処理件数の3つだけでも構いません。これがあるだけで、「動いていない」のか「動いたが0件だった」のかが即座に分かります。定期実行の障害調査の8割はここで終わります。
次に、2回走っても壊れない作りにします。同じ日のデータを何度取り込んでも結果が同じになるよう、キーで上書きするか、テーブル側に一意の制約を置いて重複を弾きます。cronの二重登録や再実行は運用のなかで必ず起きるので、起きない前提で作らないほうが安全です。取り込みの前処理で文字コードを揃えておく話はCSVの取り込みで文字化けを起こさないための処理と、確認の手順に書いています。
そして、処理時間を見ておきます。850件で6分弱という実測があれば、件数が数千に増えたときにPHPの実行時間や共用サーバーの負荷制限に触れることが予測できます。増える見込みがあるなら、最初から分割実行にしておくほうが後の作業が減ります。定期実行の維持まで含めて任せたい場合の範囲は保守プランのページに整理しています。
残るのは、止まったことに気づく仕組みです。管理画面に最終実行時刻を出す、件数が0だったら通知する。この程度でも、止まったまま1か月が過ぎる事態は避けられます。外部サービスで組んでいた自動化を自前に切り替える判断については、Zapierで足りなくなったとき、自作APIブリッジに切り替える判断基準も参考になります。
よくある質問
予約投稿が「予約投稿の失敗」と表示されて公開されません
公開予定時刻を過ぎてから、WP-Cronが呼ばれるまでの間隔が空きすぎたときに出ます。アクセスが少ない時間帯に予約している場合はほぼこれです。対処は、サーバー側のcronから定期的に叩くようにするか、キャッシュ系プラグインの設定でWP-Cronの呼び出しが抑制されていないかを確認することです。すでに失敗表示になった記事は、手で公開に切り替えれば出ます。
アクセスがほとんど無いサイトでは、どうすればいいですか
サーバー側のcronに寄せてください。共用のレンタルサーバーでも、管理画面からcronを登録できるところがほとんどです。頻度は用途しだいですが、予約投稿やバックアップが主目的なら5分から15分間隔で十分です。1分間隔にしても得るものは少なく、共用サーバーでは負荷の面で嫌われます。
外部の監視サービスから定期的にURLを叩く方法は使えますか
使えます。wp-cron.php を外から呼べば同じことが起きます。ただし、Basic認証をかけている環境やIPで制限している環境では弾かれるので、その場合は除外設定が必要です。また、外部から叩く方式は、そのサービスが止まったときに定期実行も止まる点に注意してください。サーバー内のcronのほうが依存先が少なく、原因の切り分けも簡単です。
定期実行の不具合は、動いていないことに気づくまでの時間がそのまま損失になります。何が止まっているか分からない状態でも、ログの有無と登録内容から追いかけられます。
