SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-25

Как отключить wp-cron и настроить системный cron

WP-Cron запускается только при посещении сайта, что вызывает задержки. Настройте выполнение задач через системный cron и WP-CLI для стабильной работы планировщика WordPress.

Что такое wp-cron и почему системный cron его заменяет

WP-Cron — это встроенный в WordPress планировщик задач, который запускается только при обращении пользователя к странице. Внутри WordPress ни один процесс не запускается самостоятельно. При каждом запросе, который не был обслужен из кэша, WordPress считывает список запланированных задач. Если срок выполнения наступил, он отправляет повторный HTTP-запрос самому себе по адресу /wp-cron.php для выполнения работы. Перенос этой задачи в системный cron обеспечивает предсказуемый запуск по фиксированному расписанию, независимо от того, было ли на сайте тысяча посетителей в эту минуту или ни одного.

Фактическую работу выполняют две строки: константа в wp-config.php и запись в crontab. Всё остальное в этом руководстве — это детали, которые не охватывают эти две строки. От имени какого пользователя должна выполняться задача, как подтвердить, что запланированные события действительно сработали, и три способа, которыми эта настройка может выйти из строя без вывода сообщений на сайте.

В примерах используются /srv/www/example.com в качестве директории WordPress и www-data в качестве пользователя веб-сервера. Везде подставляйте свои пути и имя пользователя.

Какой посетитель инициирует выполнение cron на нагруженном сайте

Каждый некэшированный запрос оплачивает проверку. WordPress загружает опцию cron, сравнивает метки времени, и если пришло время выполнения, вызывает spawn_cron(), который отправляет неблокирующий loopback-запрос к /wp-cron.php. Посетитель не ждет результата. Его ждет PHP-воркер. На небольшом VPS с PHP-FPM и pm.max_children = 5 одно медленное запланированное задание занимает пятую часть ваших PHP-ресурсов на все время выполнения, и вероятнее всего, оно будет запущено в самую нагруженную минуту, так как именно в этот момент происходит больше всего загрузок страниц.

WordPress ограничивает дубликаты. Он устанавливает блокировку, время жизни которой составляет WP_CRON_LOCK_TIMEOUT, по умолчанию 60 секунд, поэтому одновременные посетители не запускают процесс каждый по отдельности. Блокировка ограничивает дублирование. Она не выносит выполнение задачи за пределы пути запроса.

Подсчитайте, как часто он срабатывает на вашем сервере, прежде чем решать, имеет ли это значение. Каждый loopback-запрос отображается в access log веб-сервера:

sudo grep -c 'wp-cron.php' /var/log/nginx/access.log

Apache вместо этого пишет в /var/log/apache2/access.log. Тысячи срабатываний в день — это реальные затраты, и это то число, которое следует измерить на собственной машине, а не читать в статье, точно так же, как вы бы сделали бенчмарк VPS до и после любого другого изменения.

Кэширование меняет картину. Если кэш страниц обслуживает большинство запросов как статический HTML, PHP для этих запросов не запускается, поэтому проверка cron никогда не происходит. Нагруженный сайт с активным кэшированием начинает вести себя как малонагруженный сайт, описанный ниже.

Почему посещения сайта влияют на работу cron

Отсутствие посетителей означает отсутствие выполнения cron. Сайт, который получает лишь несколько посещений в день, запускает запланированные задачи столько же раз, в случайные моменты времени, когда приходят эти посетители.

Симптомы всегда выглядят одинаково. Запись, запланированная на 09:00, остается в списке записей со статусом Missed schedule до тех пор, пока кто-нибудь не загрузит страницу. Плагины для резервного копирования пропускают ночные задания. Проверки обновлений задерживаются, поэтому на панели управления не отображаются доступные обновления, даже если уже вышел патч безопасности. Письма о заказах, уведомления о продлении и предупреждения об истечении срока действия отправляются с опозданием.

Ни одно из этих событий не фиксируется в логах как ошибка. С точки зрения WordPress задача не была просрочена, так как она попросту не была запущена.

Шаг 1: отключение триггера посетителей в wp-config.php

Откройте /srv/www/example.com/wp-config.php и добавьте константу:

define( 'DISABLE_WP_CRON', true );

Разместите её перед строкой, содержащей /* That's all, stop editing! Happy publishing. */, так как строка сразу после этого комментария требует wp-settings.php, а wp-settings.php — это место, где WordPress привязывает проверку cron к init. Константа, определённая после этого require, устанавливается слишком поздно, чтобы на что-то повлиять: файл выглядит корректно, но триггер продолжает работать.

Убедитесь, что строка находится там, где вы предполагаете:

grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.php

Эта константа не останавливает планирование событий. Плагины продолжают добавлять задачи в очередь, как и раньше. Она лишь запрещает загрузчикам страниц запускать эту очередь, что означает, что очередь теперь не будет выполняться до завершения шага 3.

Она также не блокирует прямые запросы к /wp-cron.php. Любой пользователь по-прежнему может запросить этот URL, что обычно безопасно, так как файл выполняет только те задачи, срок которых наступил. Блокировка этого файла в конфигурации веб-сервера является опциональной. Если вы всё же заблокируете его, резервный механизм curl, описанный в конце этого руководства, также перестанет работать.

Шаг 2: установка WP-CLI

WP-CLI — это официальный инструмент командной строки для WordPress. Ему требуется PHP-бинарный файл для командной строки, который поставляется в виде отдельного пакета, отличного от модуля PHP для веб-сервера.

php -v
sudo apt install -y php-cli

Установите WP-CLI из phar-сборки, как рекомендует официальное руководство по установке:

cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --info

php wp-cli.phar --info выводит путь к бинарному файлу PHP, версию PHP и версию WP-CLI. Если выводятся все три параметра, значит, phar работает корректно. По состоянию на август 2026 года в руководстве по установке указана версия PHP 7.2.24 как минимально допустимая, а Ubuntu 24.04 поставляется с PHP 8.3, поэтому текущая версия сервера полностью соответствует требованиям. Обновление выполняется позже с помощью sudo wp cli update.

Запускайте WP-CLI от имени пользователя сайта, никогда не используйте root:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core version

От имени root WP-CLI отказывается запускаться:

Error: YIKES! It looks like you're running this as root.

Инструмент предлагает использовать --allow-root. Не используйте этот флаг здесь. Причина описана в первом сценарии сбоя ниже.

Также обратите внимание, что sudo -u www-data -i не работает, так как оболочкой входа для этой учетной записи является /usr/sbin/nologin, и вы получите This account is currently not available.. Передача команды напрямую в sudo -u позволяет обойти оболочку входа, поэтому она выполняется успешно.

Теперь убедитесь, что сам WordPress видит константу из шага 1:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'

Команда должна вывести bool(true). Фатальная ошибка о неопределенной константе означает, что строка define() не была достигнута, что обычно указывает на то, что она была размещена после инструкции require.

Шаг 3: добавление записи в cron от имени нужного пользователя

Правильный пользователь — это владелец файлов, в которые пишет PHP. Проверьте оба параметра:

stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.conf

В стандартной установке Ubuntu оба значения равны www-data. Если вы создали для сайта отдельный пул PHP-FPM с собственным пользователем, что обычно является финальным этапом настройки стека LAMP на Ubuntu 24.04, используйте этого пользователя для всех последующих действий.

Создайте каталог для логов, в который этот пользователь может записывать данные:

sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cron

Отредактируйте crontab этого пользователя:

sudo crontab -u www-data -e

Добавьте одну строку:

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1

Разберем по частям. */5 запускает задачу каждые пять минут. flock -n использует файл блокировки и немедленно завершает работу, если предыдущий процесс еще выполняется. /usr/local/bin/wp — это абсолютный путь, который необходим для cron. --path позволяет команде выполняться из любой рабочей директории. --due-now запускает только те события, время которых подошло, вместо обработки всей очереди. Перенаправление вывода отправляет стандартный вывод и ошибки в один файл, который вы сможете прочитать.

На практике это перенаправление обязательно. Cron отправляет вывод задания на почту пользователя, но в большинстве образов VPS не установлен агент передачи почты (MTA), поэтому cron записывает (CRON) info (No MTA installed, discarding output) и удаляет вывод. Файл позволяет сохранить данные для анализа.

Проверьте, что файл был сохранен:

sudo crontab -u www-data -l

Для нескольких сайтов добавьте по одной строке для каждого, разнеся их по времени, чтобы они не запускались одновременно:

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1

Лог будет расти бесконечно, если его не ротировать. Создайте файл /etc/logrotate.d/wp-cron:

/var/log/wp-cron/*.log {
    weekly
    rotate 4
    missingok
    notifempty
    compress
    create 640 www-data www-data
}

Проверьте корректность синтаксиса без внесения изменений: sudo logrotate --debug /etc/logrotate.d/wp-cron.

Шаг 4: подтверждение выполнения запланированных событий

Успешное сохранение строки в crontab еще ничего не гарантирует. Выполняйте проверку от самых простых методов к тем, которые дают окончательный ответ.

Во-первых, запустил ли cron вашу команду? Cron записывает логи в журнал под своим собственным юнитом:

journalctl -u cron.service --since "15 min ago" | grep wp

Корректная запись выглядит следующим образом (временная метка и имя хоста в начале опущены):

CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)

Эта строка означает, что cron запустил вашу команду от имени www-data. Она не дает информации о том, была ли команда выполнена успешно.

Во-вторых, выполнил ли что-либо WordPress? Прочитайте файл лога:

sudo tail -n 20 /var/log/wp-cron/example.log

WP-CLI выводит по одной строке на каждое событие, а затем итоговую информацию:

Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.

Ошибки попадают в тот же файл, в этом и заключается основная цель 2>&1. В большинстве случаев, если запланированных задач нет, файл будет содержать минимум данных, поэтому проверяйте его после того запуска, когда вы точно знаете, что задачи должны были быть выполнены.

В-третьих, выполните сквозную проверку. Запланируйте маркерное событие и проследите за его исчезновением:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_check

Подождите один интервал, затем снова выполните команду вывода списка. Хук должен исчезнуть, так как однократное событие удаляется из очереди после выполнения. Поскольку ни один плагин не регистрирует обратный вызов для этого имени хука, его выполнение не окажет никакого влияния на сайт. Если хук все еще отображается в списке после двух интервалов, значит, очередь не обрабатывается, а первые две проверки помогут вам понять, в чем проблема: в cron или в WP-CLI.

Не используйте wp cron test для этой цели. Эта команда проверяет, работает ли запуск задач через посещения сайта, и она выдает ошибку, если установлено значение DISABLE_WP_CRON. На правильно настроенном сервере эта ошибка является ожидаемым результатом, а не признаком неисправности.

Альтернатива в виде systemd timer

Если остальные запланированные задачи на сервере уже выполняются через systemd services and timers, перенесите туда и WordPress. Каждый запуск будет отображаться в systemctl list-timers, а вывод будет направляться в журнал, что избавляет от необходимости ротации лог-файлов.

Создайте /etc/systemd/system/wp-cron-example.service:

[Unit]
Description=Run due WordPress cron events for example.com

[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

Затем создайте /etc/systemd/system/wp-cron-example.timer:

[Unit]
Description=Run WordPress cron for example.com every 5 minutes

[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20

Systemd не запускает две копии одного и того же сервиса одновременно, поэтому данная версия не требует flock. Параметр Persistent=true позволяет выполнить пропущенную задачу, если сервер был выключен, чего нельзя добиться с помощью crontab.

Выберите что-то одно: crontab или таймер. Использование обоих методов для одного сайта приведет к тому, что очередь будет обрабатываться дважды, а дублирующиеся запуски задач по отправке email или обработке заказов будут заметны вашим клиентам.

Почему cron-задача не должна выполняться от имени root

Это первый из трех способов, которыми настройка может выйти из строя. Если добавить задачу в crontab пользователя root, WP-CLI остановится, не выполнив никаких действий:

Error: YIKES! It looks like you're running this as root.

Очередь никогда не будет обработана, и если вы не перенаправили вывод, вы не увидите сообщение об ошибке. Опасное решение — добавление --allow-root, так как после этого все файлы, созданные плагином во время выполнения, будут принадлежать пользователю root. Следующий веб-запрос выполняется от имени www-data, не может записать данные в эти каталоги, и сайт начинает выдавать ошибки вида:

Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?

Исправьте владельца файлов, а затем перенесите задачу:

sudo chown -R www-data:www-data /srv/www/example.com/wp-content

Crontab пользователя root и crontab пользователя www-data — это разные файлы, поэтому удаление строки из одного не затрагивает другой. Проверьте оба:

sudo crontab -u root -l
sudo crontab -u www-data -l

Почему cron сообщает об ошибке wp: not found

Это уже второй сбой. Cron предоставляет пользовательским задачам очень короткий PATH, /usr/bin:/bin. WP-CLI устанавливается в /usr/local/bin, которого нет в этом списке. Задача запускается, завершается с ошибкой за долю секунды, и в логе остается одна строка:

/bin/sh: 1: wp: not found

Вместо догадок посмотрите на окружение cron самостоятельно. Добавьте временную строку:

*/5 * * * * env > /tmp/cron-env.txt 2>&1

Прочитайте /tmp/cron-env.txt после одного интервала выполнения, затем удалите эту строку. Значение PATH= в этом файле — это именно то, что получает ваша задача.

Существует два способа исправления. Используйте абсолютный путь /usr/local/bin/wp, как в шаге 3. Либо задайте PATH один раз в начале crontab, перед всеми строками задач:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Та же ловушка подстерегает уровнем ниже. Файл wp phar начинается с #!/usr/bin/env php, поэтому оболочка должна иметь возможность найти и php. Если PHP находится вне /usr/bin, что случается при использовании пользовательских сборок или сборок панелей управления, вы получите:

/usr/bin/env: 'php': No such file or directory

В этом случае вызывайте интерпретатор явно, например, /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now.

Почему интервал в одну минуту воссоздает исходную проблему

Это уже третий сбой. * * * * * кажется более безопасным вариантом, чем пять минут, но на нагруженном сайте это возвращает вас к исходной точке. Если выполнение задачи занимает больше времени, чем заданный интервал, следующий запуск начнется, пока предыдущий еще не завершился. Через десять минут будет запущено десять процессов PHP, каждый из которых занимает свою память и удерживает собственное соединение с базой данных.

Ищите признаки накопления процессов напрямую:

ps -eo etimes,user,args | grep '[c]ron event run'

etimes — это время жизни процесса в секундах. Одна строка означает нормальную работу. Несколько строк с возрастом, значительно превышающим ваш интервал, означают, что запуски накладываются друг на друга. На небольшом VPS это заканчивается ошибкой Too many connections в MySQL или принудительным завершением PHP ядром для освобождения памяти, что можно подтвердить с помощью sudo dmesg -T | grep -i 'killed process'.

WP-CLI выполняет обратные вызовы событий напрямую, а не через запрос к wp-cron.php, поэтому 60-секундная блокировка, которую WordPress использует против дублирующих запусков, здесь не работает. flock -n в записи шага 3 — это то, что предотвращает наложение процессов сейчас. Пропущенный запуск завершается немедленно и без вывода сообщений, так и было задумано. Наложение процессов — это проблема, которую нужно решать в crontab, а не та, которую ядро хоста решит за вас: даже размещение задач с учетом кэша, добавленное в ядро Linux 7.2, определяет лишь то, на каком ядре окажется процесс, но не количество запущенных вами копий.

Выберите интервал, исходя из самого короткого расписания, от которого вы действительно зависите, и сначала замерьте время выполнения:

time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

Пять минут — разумное значение по умолчанию: публикация, запланированная на 09:00, выйдет к 09:05. Пятнадцать минут подходят для сайта, где нет критичных по времени задач. Одна минута нужна магазинам и плагинам, работающим с очередями, которым это действительно необходимо, и только если вы уверены, что выполнение задачи занимает несколько секунд.

Если вы не можете установить WP-CLI

Некоторые хостинг-провайдеры блокируют инструменты командной строки. Обычный HTTP-запрос к wp-cron.php запускает ту же очередь, но через весь веб-стек:

*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/null

От чего вы отказываетесь:

  • Время выполнения ограничено таймаутами веб-сервера и PHP-FPM, поэтому длительная задача может быть прервана на середине.
  • Сертификат должен быть действительным, иначе curl завершится с ошибкой SSL certificate problem, поэтому следите за обновлением сертификатов через Certbot on nginx.
  • Кэширование страниц не должно затрагивать wp-cron.php, иначе запросы cron будут получать кэшированный ответ, и задачи не выполнятся.
  • Вы не получаете вывод в реальном времени, поэтому единственным доказательством выполнения задачи будет её результат.

Флаг -sS подавляет вывод curl при успешном выполнении, но отображает ошибки, что и требуется для cron-задачи.

Что еще стоит включить в расписание сервера

Как только системный cron берет на себя управление очередью WordPress, перенесите остальные регулярные задачи сервера в то же место, где их можно легко отслеживать. Обновления безопасности операционной системы лучше доверить unattended upgrades, а не прописывать их в cron вручную. Обновление плагинов и тем WordPress — это отдельный вопрос: wp plugin update --all в crontab может привести к сбою рабочего сайта в 3 часа ночи без присмотра, поэтому выполняйте такие операции осознанно, предварительно протестировав их на staging-сервере и создав резервную копию.

FAQ

Отключение WP-Cron мешает публикации запланированных записей?

Нет, если очередь обрабатывается другим способом. DISABLE_WP_CRON лишь отключает запуск очереди при загрузке страниц. События по-прежнему планируются как обычно. Запись, запланированная на 09:00, публикуется при первом запуске cron после 09:00, поэтому при пятиминутном интервале она будет опубликована не позднее 09:05. Если вы установите константу, но не добавите запись в cron, запись останется в списке со статусом Missed schedule до тех пор, пока очередь не будет запущена.

От имени какого пользователя должен выполняться cron-задание WordPress?

От имени пользователя, которому принадлежат файлы, записываемые PHP; в стандартной установке Ubuntu это www-data. Проверьте владельца с помощью stat -c '%U %G' /srv/www/example.com/wp-content/uploads и сравните его со строкой user = в конфигурации пула PHP-FPM. Запуск задания от имени root приводит к остановке WP-CLI с ошибкой YIKES, а принудительный запуск через --allow-root создает внутри wp-content файлы, принадлежащие root, в которые веб-сервер впоследствии не сможет записывать данные.

Как часто системный cron должен запускать cron WordPress?

Для большинства сайтов подходит интервал в пять минут. Подберите интервал в соответствии с самым коротким расписанием, на которое вы действительно полагаетесь, и держите его с запасом относительно времени выполнения одного цикла. Время выполнения можно измерить, добавив time перед командой WP-CLI. На загруженных сайтах интервалы в одну минуту приводят к наложению процессов друг на друга, если они не защищены с помощью flock.

Почему wp cron test выдает ошибку после отключения WP-Cron?

Потому что эта команда проверяет запуск через посещения пользователей и сообщает об ошибке, когда DISABLE_WP_CRON установлено в true. Это ожидаемый результат для сервера, настроенного таким образом. Вместо этого проверьте путь системного cron: прочитайте /var/log/wp-cron/example.log или запланируйте контрольное событие с помощью wp cron event schedule и убедитесь, что оно исчезло из wp cron event list после следующего запуска.

Нужен ли мне WP-CLI или достаточно curl для wp-cron.php?

Curl работает, и это подходящее решение, если вы не можете установить WP-CLI. Он медленнее, так как загружает WordPress через веб-сервер и ограничен тайм-аутом запроса. WP-CLI выполняет события в процессе PHP командной строки без веб-тайм-аута и выводит по одной строке на каждое событие с указанием длительности, поэтому в логе будет точно видно, что именно выполнилось и сколько времени это заняло.