深夜に予約したテストメールが、1通も届いていませんでした。届いたのは翌朝、私がサイトを開いたその瞬間に、止まっていたぶんがまとめて。半日追いかけて行き着いた原因は、メールでも自分のコードでもなく、WP-Cron への思い込みでした。
WP-Cron は、時刻になったら動くのではなく、誰かがアクセスした瞬間に動く仕組みです。月間 PV 600 の個人ブログに、深夜のアクセスはありません。動かなかったのではなく、そもそも呼ばれていなかった。開発中の Thanks Mail for Stripe Custom を検証していたときの話です。
検証環境:WordPress 6.8.3 / Xserver スタンダード / PHP 8.3.21 / WP Rocket。検証は2026年1月、記事更新は2026年5月5日。コードや設定値は私の環境で動いた最小サンプルで、本番では環境に合わせて調整してください。
なぜ深夜だけ止まったのか
名前に Cron と付くので、Linux の cron のように時刻で動くものだと思っていました。公式の Plugin Handbook を読み直すと、動き方が違います。誰かがページを開いた瞬間に、WordPress が実行予定を過ぎたタスクはあるかを確認して、あればその場で実行する。アクセスがチェックの引き金で、引き金が引かれなければ予定の確認すら走りません。昼は動き、夜は遅れ、深夜は止まる。メールの問題ではなく、アクセス量に紐づいた問題でした。
強いページキャッシュが効いているサイトでは、これがさらに起きやすくなります。アクセスがあってもキャッシュから返してしまい、WordPress 本体までリクエストが届かないと、WP-Cron のチェックは走りません。raplsworks.com は WP Rocket を使っているので、なおさら呼ばれにくい構成でした。
まず、サーバー cron で直す
アクセス頼みが原因なら、WordPress とは別のところから定期的に WP-Cron を呼べばいい。やることは2つです。wp-config.php にアクセス起動を止める1行を足すことと、Xserver のサーバー cron で wp-cron.php を一定間隔で叩くことです。
| 1 | define('DISABLE_WP_CRON', true); |
これは「That’s all, stop editing!」のコメントより上に置きます。注意したいのは、この1行だけ入れてサーバー cron を設定しないと、予約投稿もメール送信も全部止まることです。必ず次の cron 設定までセットで終わらせてください。Xserver のサーバーパネルの Cron 設定で、分を */5、ほかを * にして、5分ごとに wp-cron.php を呼びます。間隔は、許容できる遅れから決めます。
| 間隔 | 向いている用途 |
|---|---|
| 5分ごと | 予約メール、予約投稿、通知。遅れを小さくしたいとき |
| 10分ごと | 定期通知、軽いチェック。多少の遅れを許せるとき |
| 15分ごと | バックアップ、集計。負荷を抑えたいとき |
コマンド欄には、最初こう入れていました。末尾の捨て記述がないと、5分ごとに完了メールが届きます。
| 1 | /usr/bin/curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1 |
これで、深夜のメールも5分以内に届くようになりました。最初の症状は、いったん解決です。
なぜ WP Rocket が警告を出したのか
数日運用すると、WP Rocket のダッシュボードにエラーが出ました。いくつかの予定タスク(Scheduled Cache Purge と Scheduled Database Optimization)が実行に失敗していて、CRON が正しく動いていない可能性がある、という内容です。テストメール自体は届いているのに、です。
HTTP で wp-cron.php を呼ぶ方式は、自分のサーバーが自分の URL へ外向きの HTTPS を投げる動きです。raplsworks.com では間に WP Rocket のページキャッシュが挟まっているので、cron が叩いたリクエストが、思った形で WordPress 本体まで届いていなかった。共用レンタルでは、さらに WAF やプロキシも加わるので、HTTP 経由の cron は意外と詰まりやすい構成です。この、自分が触れない中間層で思った通りに動かない感覚は、別件で WebSocket と SSE が使えず Long Polling に切り替えたときとも地続きでした(Xserver で WebSocket/SSE が使えない代替実装)。メールは届くのに WP Rocket は動いていないと言ってくる、この捻れを抱えるのは気持ちが悪い。そこで、HTTP で叩くこと自体をやめました。
WP-CLI で直接呼ぶ構成に変える
たどり着いたのが、Xserver の cron から WP-CLI 経由で WP-Cron のイベントを直接実行する構成です。WP-CLI は WordPress をコマンドラインから操作するツールで、Xserver の共用レンタルにも標準で入っています。HTTP を介さず、サーバーローカルから WordPress を直接呼ぶので、外向きの HTTPS も、WP Rocket のキャッシュも、WAF も、リダイレクトも、間に挟まりません。
| 1 | /usr/bin/wp cron event run --due-now --path=/home/[XserverアカウントID]/raplsworks.com/public_html --quiet |
cron event run –due-now が、実行予定時刻を過ぎたイベントをその場で全部実行する指定です。–path は WordPress のインストール先で、SSH で対象サイトに入って pwd を打つと正しいパスが見えます。なお、このパスには Xserver アカウント ID が含まれます。スクリーンショットを公開したりフォーラムに貼ったりするときは、ID 部分を必ずマスクしてください。私もこの記事ではマスク表記にしています。
この構成に切り替えてから、WP Rocket の失敗エラーは出なくなり、テストメールも引き続き5分以内に届いています。サーバー内部で完結するぶん、トラブル時に見るレイヤーが減るのも楽でした。Xserver の共用レンタルで、しかも WP Rocket のようなキャッシュプラグインを入れて WP-Cron を本格運用するなら、最初から WP-CLI 構成で始めるのも十分に合理的です。
二重送信を、どう防ぐか
cron が安定したら、次に気になるのが二重送信です。同じジョブが重なって走ると、同じ相手にメールが2通届くことがあります。メールは一度送ると取り消せないので、cron の安定とは別に、重複実行されても事故にならない設計を入れておきたい。シンプルなのは、WordPress のトランジェントを使った短時間ロックです。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 短時間ロックで多重実行を防ぐ $lock_key = 'my_mail_job_lock'; if ( get_transient( $lock_key ) ) { return; // すでに実行中ならスキップ } set_transient( $lock_key, 1, 5 * MINUTE_IN_SECONDS ); try { // 対象ユーザーを抽出して送信する(wp_mail など) } finally { delete_transient( $lock_key ); } |
これだけだと同時実行しか防げないので、実運用ではもう一段、ユーザー meta や独自テーブルに送信済みと記録する保険を入れています。万が一同じ処理が再実行されても、同じ相手に何度も送る事故が起きにくくなります。Stripe の決済をきっかけにメールを送る Thanks Mail for Stripe を公開したときも、ここは厳しく作りました。
動いているか、どう確かめるか
cron は設定しただけで動いているつもりになりやすいので、いくつかの角度で見ます。自分宛てのテストメールを数分後送信で予約して、5分前後で届けば WP-Cron が呼ばれている証拠です。WP Rocket を使っているなら、ダッシュボードの警告が出ていないかが、いちばん素直な指標になります。wget 構成のときはここにエラーが出ていて、WP-CLI 構成で消えました。WP-CLI ではアクセスログにリクエストが残らないので、Xserver のサーバーパネルで cron の実行履歴を見て、エラーがないこととジョブが想定通り動いていることを確認します。WP-Cron はメールだけでなく予約投稿や定期バックアップにも使われているので、メール以外に乗っている処理がないかも見ておきます。
呼ばれて、はじめて動く
WP-Cron を、時刻になったら勝手に走るものだと思い込んでいたのが、今回いちばんの落とし穴でした。実際は、誰かがアクセスしたタイミングで予定をチェックする仕組みです。raplsworks.com では、最終的にこの流れで安定させました。
| 1 | wp-config.php に DISABLE_WP_CRON を追加して、アクセス起動を止める |
| 2 | Xserver の Cron で wp-cron.php を5分ごとに呼ぶ(最初は curl / wget 構成) |
| 3 | WP Rocket の警告が出たので、WP-CLI(wp cron event run –due-now)構成へ切り替える |
| 4 | テストメール、WP Rocket、cron 実行履歴で動作を確認する |
予約メールが届かない、深夜だけ定期処理が動かない、と感じたら、メール周りを細かく見るより先に、そもそも処理が呼ばれているかを疑ってみてください。症状から見える層と、原因がある層は、よくずれます。そしてもうひとつ。最初に動いた構成が、最後まで動き続ける構成とは限りません。今回の wget から WP-CLI への切り替えが、まさにそうでした。あなたが最初にたどり着いた構成は、本当に、あなたのサーバーにとって自然な形になっているでしょうか。
参考にした情報
- WordPress Developer Resources:Plugin Handbook, Cron(確認日:2026年5月5日)
- WordPress Developer Resources:Hooking WP-Cron Into the System Task Scheduler(確認日:2026年5月5日)
- Xserver 公式マニュアル:Cron 設定(確認日:2026年5月5日)
- WP-CLI 公式ドキュメント:wp cron event run(確認日:2026年5月5日)
関連記事
- WP-Cron を時計として信じるのをやめた|予約投稿を「ズレ・重複・取りこぼし前提」で組み直した設計。この記事の続編です。cron を安定させた次は、それでも狂う前提で土台を組む話。
- Xserver の WordPress 環境で WebSocket/SSE が使えない、代替実装。同じく、自分が触れない中間層で詰まった話です。
- 特定の WordPress 記事だけ 501 Not Implemented になった話|Xserver WAF の誤検知。同じく Xserver で踏んだ WAF 関連のトラブルです。




コメント