SSD Nodes Learn 🎉 VPS от $4.99/мес
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-07

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

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

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

WP-Cron — это планировщик задач, встроенный в WordPress. Он запускается только тогда, когда кто-то запрашивает страницу. Внутри 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 не работает, так как командной оболочкой (login shell) этой учетной записи является /usr/sbin/nologin, и вы получите This account is currently not available.. Передача команды напрямую в sudo -u позволяет обойти login shell, поэтому она выполняется успешно.

Теперь убедитесь, что сам 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 записывает логи в journal под своим собственным юнитом:

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 установлено в true. На правильно настроенном сервере эта ошибка является ожидаемым результатом, а не неисправностью.

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

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

Создайте /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 или timer. Использование обоих вариантов для одного сайта приведет к тому, что очередь будет обрабатываться дважды, а дублирующиеся запуски задач по отправке писем или обработке заказов станут заметны вашим клиентам.

Почему 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 это заканчивается ошибкой MySQL Too many connections или принудительным завершением PHP ядром для освобождения памяти, что можно подтвердить с помощью sudo dmesg -T | grep -i 'killed process'.

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

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

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, а не строке в crontab, которую вы поддерживаете вручную. Обновления плагинов и тем 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 командной строки без веб-таймаутов и выводит по одной строке на каждое событие с указанием длительности, поэтому в логе будет точно видно, что именно выполнялось и сколько времени это заняло.

#wordpress#cron#wp-cli#performance#vps