夜中だけ予約メールが届かない|WP-Cron は「アクセスで動く」と知らずに半日溶かした話(Xserver + WP-CLI で安定運用)

WordPress
この記事は約9分で読めます。

深夜に予約したテストメールが、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日。コードや設定値は私の環境で動いた最小サンプルで、本番では環境に合わせて調整してください。

WP-Cronのデフォルト(アクセストリガー方式)とサーバーcron方式の比較図

なぜ深夜だけ止まったのか

名前に 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 を一定間隔で叩くことです。

これは「That’s all, stop editing!」のコメントより上に置きます。注意したいのは、この1行だけ入れてサーバー cron を設定しないと、予約投稿もメール送信も全部止まることです。必ず次の cron 設定までセットで終わらせてください。Xserver のサーバーパネルの Cron 設定で、分を */5、ほかを * にして、5分ごとに wp-cron.php を呼びます。間隔は、許容できる遅れから決めます。

間隔 向いている用途
5分ごと 予約メール、予約投稿、通知。遅れを小さくしたいとき
10分ごと 定期通知、軽いチェック。多少の遅れを許せるとき
15分ごと バックアップ、集計。負荷を抑えたいとき

コマンド欄には、最初こう入れていました。末尾の捨て記述がないと、5分ごとに完了メールが届きます。

これで、深夜のメールも5分以内に届くようになりました。最初の症状は、いったん解決です。

なぜ WP Rocket が警告を出したのか

数日運用すると、WP Rocket のダッシュボードにエラーが出ました。いくつかの予定タスク(Scheduled Cache Purge と Scheduled Database Optimization)が実行に失敗していて、CRON が正しく動いていない可能性がある、という内容です。テストメール自体は届いているのに、です。

wget構成で、サーバーcronのHTTPリクエストがWP Rocketのページキャッシュ層に阻まれWordPress本体へ届かない構造図

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 も、リダイレクトも、間に挟まりません。

サーバーcronがWP-CLI経由でWordPress本体を直接呼び、HTTP・キャッシュ・WAFを経由しない構造図

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 のトランジェントを使った短時間ロックです。

これだけだと同時実行しか防げないので、実運用ではもう一段、ユーザー 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日)

関連記事

WordPress
この記事を書いた人
rapls

WordPressのプラグインを作っているフリーランスエンジニアです。Web開発はもう6年以上。WordPress.orgで Rapls AI Chatbot、Thanks Mail for Stripe、Rapls PDF Image Creator、Prime Cache の4本を公開し、保守を続けながら、日本語ロケールの翻訳エディター(PTE)も務めています。このブログに書くのは、現場で自分が実際にハマって、調べて、直した話です。

raplsをフォローする
raplsをフォローする

コメント

タイトルとURLをコピーしました