WP-Cron в WordPress: как починить и мониторить
У WordPress есть собственный планировщик — WP-Cron. Он публикует отложенные записи, отправляет письма WooCommerce, запускает плагины бэкапов и SEO и проверяет обновления. Только это не настоящий cron: он срабатывает, лишь когда кто-то открывает сайт. Поэтому у отложенных записей появляется «Пропущено расписание», а плагины бэкапов пропускают ночи. Решение — перевести его на системный cron и следить, что тот продолжает работать.
Почему WP-Cron пропускает запуски
- Мало посетителей. Нет визитов в 3 часа ночи — нет и запуска в 3 часа. Задача ждёт следующего просмотра страницы.
- Кеширование страниц. Когда CDN или плагин кеширования отдаёт страницу без запуска PHP, WordPress не получает шанса проверить расписание, даже если посетителей много.
- Заблокированный loopback. WP-Cron стартует запросом к
wp-cron.phpна собственном сайте. Файрвол, basic auth на тестовом сервере или сломанная DNS-запись ломают этот запрос. «Здоровье сайта» в админке показывает это как ошибку loopback. - Долгие задачи на запросе посетителя. Тяжёлые задачи выполняются внутри веб-запросов и упираются в лимиты времени PHP.
Отключите встроенный запуск
Добавьте строку в wp-config.php выше /* That's all, stop editing! */:
define( 'DISABLE_WP_CRON', true );
Это только запрещает запускать WP-Cron при просмотре страниц. Запланированные события остаются, а выполнять их теперь будет системный cron.
Запускайте его из системного cron
Если на сервере есть WP-CLI, выполняйте наступившие события каждые пять минут от имени пользователя, которому принадлежат файлы сайта:
# crontab -e для пользователя сайта, например www-data
*/5 * * * * cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now --quiet
Без WP-CLI, например на виртуальном хостинге, запрашивайте wp-cron.php через curl:
*/5 * * * * curl -fsS -m 60 -o /dev/null "https://example.com/wp-cron.php?doing_wp_cron"
WP-CLI лучше: он не зависит от веб-сервера, не ограничен таймаутом запроса, а по коду завершения видно, получилось ли.
Что запланировано
wp cron event list # все события, их хуки и следующий запуск wp cron schedule list # доступные интервалы wp cron event run woocommerce_cleanup_sessions # выполнить одно событие сейчас
В админке тот же список показывает бесплатный плагин WP Crontrol. События со следующим запуском в прошлом значат, что планировщик не работает.
WooCommerce и многие плагины ставят задачи в очередь Action Scheduler («Инструменты → Запланированные действия»), а она работает поверх WP-Cron. Если встанет WP-Cron, встанет и очередь: письма о заказах, продление подписок, вебхуки.
Алерт, если он остановится
Теперь расписание WordPress выполняет системный cron, значит, следить нужно за ним. Создайте в Heartcron проверку с периодом 5 минут и запасом, например, 10 минут, и добавьте пинг в строку:
*/5 * * * * cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now --quiet; curl -fsS -m 10 --retry 3 -o /dev/null https://ping.heartcron.com/<uuid>/$?
Пинг передаёт код завершения WP-CLI: 0 — проверка в порядке, любой другой — алерт сразу. Если строка из crontab пропадёт, сайт переедут на другой сервер или PHP сломается после обновления, пинг не придёт, и алерт придёт после запаса времени. Один раз выполните команду вручную и убедитесь, что она завершается с кодом 0, в том числе когда наступивших событий нет: wp cron event run --due-now; echo $?.
Важные задачи — отдельно
Одна проверка говорит, что планировщик работает. Для задачи, пропуск которой действительно заметен, например ночного бэкапа или ежедневной выгрузки, заведите отдельную проверку и пингуйте её из самой задачи. В плагине или в functions.php темы:
add_action( 'my_nightly_export', function () {
$url = 'https://ping.heartcron.com/<uuid>';
wp_remote_get( $url . '/start', [ 'timeout' => 10 ] );
try {
my_run_export();
wp_remote_get( $url, [ 'timeout' => 10 ] );
} catch ( Throwable $e ) {
wp_remote_post( $url . '/fail', [ 'timeout' => 10, 'body' => $e->getMessage() ] );
throw $e;
}
} );
Задайте проверке то же расписание, что у события. Не уверены, как записать его в формате cron? Расшифровщик cron-выражений покажет ближайшие запуски.