WP-Cronが動かない…Xserverのcronで“時間通りのメール送信”を取り戻した話

WP-Cronが動かない!サーバーCronで5分ごとに解決! WordPress
この記事は約7分で読めます。

「決まった時刻にメールを送りたい」——WordPressでサイトを運用していると、いつか必ず出てくる願いです。

たとえばこんな場面。

  • 会員向けに「明日のイベントのリマインド」を自動送信したい
  • 申し込みから数日後にフォローメールを送って、離脱を減らしたい
  • 毎週決まった曜日に、レポートを担当者へ送信したい

私も同じことを考えて、WordPress側でスケジュールを組み、メール送信処理を登録しました。準備は完璧……のはずでした。

ところが、深夜〜早朝だけメールが送られない。
管理画面上は「スケジュール登録できている」ように見えるのに、肝心の送信が起きない。ログを追っても、送信処理そのものが呼ばれていない。地味に見えて、運用としては致命的でした。

この記事では、私がハマった原因(WP-Cronの仕様)と、XserverのcronでWP-Cronを5分ごとに動かして安定運用に持ち込んだ手順を、体験談としてまとめます。


起きたこと:スケジュールはあるのに送られない

最初に疑ったのはメール周りでした。

  • SMTPの設定が悪い?
  • 迷惑メール判定で落ちている?
  • 宛先側の受信制限?

でも調べるほど、違和感が増えます。なぜなら「届かない」ではなく、送信処理が“走っていない”からです。

そして妙に条件が揃っていました。

  • 昼間は動く
  • アクセスが多い時間帯は動く
  • 深夜〜早朝(アクセスが少ない時間)だけ止まる

この時点で、ようやく「WP-Cronかもしれない」と疑い始めました。ここが私の反省ポイントです。WordPressでスケジュールを組むと、つい「cron=時間で勝手に回る」と思いがちなんですよね。


原因:WP-Cronは“アクセスがトリガー”になりがち

WordPressのWP-Cronは、OSのcron(サーバーのcron)のように常駐して定期実行する仕組みではありません。

多くの環境では、サイトへのアクセスをきっかけに「期限が来ているイベントはあるか?」を確認し、あれば実行します。

つまり、こうなります。

  • アクセスが少ない(あるいはゼロ)→ cronが起動しない/遅れる
  • アクセスが多い → cron起動回数が増えて、場合によっては負荷になり得る

私のサイトは、深夜にアクセスがほとんど無い日がありました。結果、メール送信の予定時刻を過ぎてもWP-Cronが走らず、「送られるはずのメール」が“保留”のままになっていたわけです。

この仕様は、低トラフィックサイトだけでなく、強力なページキャッシュが効いて「WordPressまでリクエストが届きにくい」構成でも、似たような症状が出ます。


解決策:Xserverのcronでwp-cron.phpを5分ごとに起動

結論はシンプルです。

  1. WordPress側の「アクセス時にWP-Cronを起動する」動作を止める
  2. Xserverのcronで、定期的に wp-cron.php を叩く

私は5分ごとにしました。理由は、メール送信を「だいたい時間通り」にしたかったのと、負荷を増やし過ぎたくなかったからです。用途次第で10分や15分でも十分なケースは多いです。


手順:WordPress設定 → Xserver cron設定

ステップ1:wp-config.php に DISABLE_WP_CRON を追加

重要:これを入れたら、サーバーcronを設定するまでWP-Cronは動かなくなります。先に手順全体を把握してから作業するのがおすすめです。

wp-config.php に以下を追加します(「That’s all, stop editing!」より上が定番)。

define('DISABLE_WP_CRON', true);

これで「アクセスが来たタイミングでcronを起動する」挙動を止めます。

ステップ2:Xserverのサーバーパネルでcronを設定(5分ごと)

Xserverはサーバーパネルからcronを設定できます。やることは「5分ごとにURLを叩く」だけです。

  1. サーバーパネルにログイン
  2. Cron設定を開く
  3. 実行間隔を5分ごとに設定(分に */5 を指定)
  4. コマンドに wp-cron.php を叩く処理を入れる

設定例(curl推奨)。

*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

wgetでもOKです。

*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

ポイント:

  • example.com は自分のドメインに置換
  • > /dev/null 2>&1 は出力を捨ててログ肥大化を防ぐ
  • サイトがhttps運用なら必ずhttpsで叩く

動作確認:本当に回っているか確かめる

設定して「たぶん動いた」で終わるのが一番危ないので、私は次の確認をしました。

1) テスト用に“すぐ送る”スケジュールを作る

たとえば「1分後に自分へ送る」テストイベントを作って、5分以内に届くか確かめます。届いたらまず一安心です。

2) wp-cron.php のアクセスが増えているかを見る

サーバーのアクセスログで、wp-cron.php への定期アクセスが入っているか確認します(見られる範囲でOK)。

3) 管理画面で“遅延が解消した感”があるか

予約投稿や定期処理がある場合、時間ズレが減っていれば成功率は高いです。私はメール送信が時間通りになったので、ここで確信に変わりました。


運用のコツ:重複送信・暴発・失敗時の備え

cronが安定すると次に気になるのが「重複送信しないか」です。実はここ、運用で地味に効きます。

たとえば、何らかの理由でcronが同時実行されたり、処理が重くて次の起動と重なったりすると、同じ対象にメールを2回送る事故が起きます。

そこでおすすめなのが、短時間ロック(トランジェント)です。以下は考え方のサンプルです(そのままコピペするより、設計に合わせて調整してください)。

// 疑似コード例:短時間ロックで多重実行を防ぐ
$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);
}

さらに確実にするなら、

  • 「送信済みフラグ」をDBに保存する(ユーザーmetaや独自テーブル)
  • ジョブID(日付+種別)で一意にする
  • 失敗時は再送キューに入れる

といった設計に寄せると、事故率がぐっと下がります。


FAQ

Q. DISABLE_WP_CRON を入れると、何が止まりますか?

A. 予約投稿、定期バックアップ、各種プラグインの定期処理など、WP-Cronに依存しているものは止まります。だからこそ、サーバーcronとセットで運用します。

Q. サーバーcronとWP-Cron(アクセス起動)を両方動かしたら?

A. 起動回数が増え、重複実行や負荷の原因になることがあります。基本はどちらか一方に寄せるのがおすすめです(本記事ではサーバーcronに寄せます)。

Q. 5分ごとじゃないとダメ?

A. 用途次第です。厳密な時刻が重要なら5分、ゆるい通知なら10分〜15分でも十分な場合が多いです。まずは運用上「遅れて困る許容範囲」から決めるのが現実的です。

Q. Basic認証やアクセス制限がある場合は?

A. HTTPで叩く方式は制限の影響を受けます。wp-cron.php だけ例外にする、あるいはサーバー側で別の実行方法を検討する必要があります。


まとめ

今回の学びは、ひとことで言うとこれでした。

「WP-Cronは“時間で勝手に回る”と思い込むと、静かに詰む」

WP-Cronは便利ですが、アクセスが少ない時間帯に処理が遅れたり止まったりすることがあります。私はそこに気付けず、スケジュールメールが送れない状態で悩みました。

最終的には、

  • define('DISABLE_WP_CRON', true); でアクセス起動を止める
  • Xserverのcronで wp-cron.php5分ごとに起動する

この構成にして、時間通りのメール送信を取り戻せました。

もしあなたも「予約メールが送れない」「深夜だけ動かない」といった症状に心当たりがあるなら、一度WP-Cronの起動条件を疑ってみてください。運用の安定度が、びっくりするくらい変わります。

コメント

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