Як вимкнути WP-Cron і налаштувати system cron
WP-Cron запускається лише під час відкриття сторінок: на тихих сайтах зависає, на завантажених накопичує чергу. Перенесіть запуск у system cron через WP-CLI та перевірте виконання.
Що таке wp-cron і чому його замінює system cron
WP-Cron — це планувальник завдань, вбудований у WordPress. Він запускається лише тоді, коли хтось відкриває сторінку. Нічого всередині WordPress не запускається самостійно. Під час кожного запиту, який не обслуговується з кешу, WordPress читає список запланованих завдань. Якщо для якогось завдання настав час, WordPress надсилає додатковий HTTP-запит до самого себе на /wp-cron.php, щоб виконати це завдання. Перенесення цього завдання до system cron забезпечує один передбачуваний запуск за фіксованим розкладом — незалежно від того, чи було на сайті тисяча відвідувачів протягом цієї хвилини, чи жодного.
Фактичну роботу виконують два рядки: константа у wp-config.php і запис у crontab. Усе інше в цьому посібнику стосується того, чого не видно з цих двох рядків: від якого користувача має запускатися завдання, як перевірити, що заплановані події справді виконалися, і які є 3 способи непомітної відмови конфігурації без повідомлення на сайті.
У прикладах використано /srv/www/example.com як каталог WordPress і www-data як користувача веб-сервера. Усюди замініть ці значення на власні шляхи та ім’я користувача.
Який відвідувач спричинив витрати cron на завантаженому сайті
Кожен запит без кешу оплачує цю перевірку ресурсами. WordPress завантажує опцію cron, порівнює часові мітки, а коли настає час виконання завдання, викликає spawn_cron(). Ця функція надсилає неблокувальний loopback-запит до /wp-cron.php. Відвідувач не чекає на результат. Але PHP worker очікує. На невеликому VPS із PHP-FPM і pm.max_children = 5 одне повільне заплановане завдання займає п’яту частину доступної потужності PHP на весь час виконання. Найімовірніше, воно запуститься саме протягом найзавантаженішої хвилини, оскільки тоді надходить найбільше запитів на сторінки.
WordPress обмежує дублікати. Він встановлює блокування з часом життя WP_CRON_LOCK_TIMEOUT — за замовчуванням 60 seconds. Тому одночасні відвідувачі не запускають окремий процес кожен. Блокування обмежує дублювання. Але воно не прибирає виконання завдання зі шляху обробки запиту.
Перш ніж вирішувати, чи має це значення, перевірте частоту запусків на власному сервері. Кожен loopback-запит з’являється в access log веб-сервера:
sudo grep -c 'wp-cron.php' /var/log/nginx/access.logApache натомість записує їх до /var/log/apache2/access.log. Кілька тисяч таких запитів на день — це реальні витрати. Такі показники потрібно вимірювати на власному сервері, а не брати зі статті. Так само слід порівняти продуктивність VPS до та після будь-якої іншої зміни.
Кешування змінює ситуацію. Якщо page cache обслуговує більшість запитів як статичний 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, і зазвичай це безпечно, оскільки файл запускає лише завдання, для яких настав час. Блокування цього 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 --infophp wp-cli.phar --info виводить шлях до бінарного файлу PHP, версію PHP і версію WP-CLI. Якщо виведено всі три значення, phar працює. Станом на August 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 pool із власним користувачем, що зазвичай є результатом окремого налаштування сайту в LAMP stack на 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 захоплює lock file і негайно завершує роботу, якщо попередній запуск досі утримує блокування. /usr/local/bin/wp — це абсолютний шлях, який потрібен cron. --path дає змогу виконувати команду з будь-якого робочого каталогу. --due-now обробляє лише події, час яких настав, а не кожну подію в черзі. Перенаправлення записує стандартний вивід і помилки в один файл, який можна прочитати.
На практиці це перенаправлення обов’язкове. Cron надсилає вивід завдання його користувачу електронною поштою, але в більшості VPS-образів не встановлено mail transfer agent. Тоді 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 записує повідомлення до журналу через власний unit:
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.logWP-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Зачекайте один інтервал, а потім знову виконайте команду для перегляду списку. Hook зникне, оскільки одноразова подія видаляється з черги після виконання. Жоден плагін не реєструє callback для цього імені hook, тому його запуск більше нічого не змінює на сайті. Якщо після двох інтервалів hook усе ще відображається у списку, черга не обробляється. Перші дві перевірки покажуть, проблема в cron чи в WP-CLI.
Не використовуйте для цього wp cron test. Ця команда перевіряє, чи працює запуск процесу за запитом відвідувача, і повертає помилку, коли виконується умова DISABLE_WP_CRON. На правильно налаштованому сервері ця помилка є очікуваним результатом, а не несправністю.
Альтернатива з timer systemd
Якщо інші заплановані завдання на сервері вже виконуються через сервіси й timer systemd, додайте туди й 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.targetsudo 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 20systemd не запускатиме одночасно дві копії одного сервісу, тому цій версії не потрібен flock. Persistent=true дає змогу виконати пропущений запуск після вимкнення машини. Запис у crontab цього не робить.
Виберіть crontab або timer. Якщо запускати обидва варіанти для одного сайту, черга оброблятиметься двічі. Повторні запуски завдань електронної пошти або замовлень будуть помітні вашим клієнтам.
Чому cron job не повинен запускатися від імені root
Це перший із трьох способів, через які налаштування не працює. Додайте job до crontab root, і WP-CLI завершить роботу ще до виконання будь-яких дій:
Error: YIKES! It looks like you're running this as root.Черга не запускається. Якщо ви не перенаправили вивід, повідомлення не буде видно. Небезпечне виправлення — додати --allow-root, оскільки тоді всі файли, які plugin створює під час цього запуску, належатимуть root. Наступний вебзапит виконується від імені www-data і не може записувати до цих каталогів. Сайт починає повідомляти, наприклад:
Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?Виправте власника, а потім перенесіть job:
sudo chown -R www-data:www-data /srv/www/example.com/wp-contentcrontab 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Ця сама проблема виникає на наступному рівні. Файл phar wp починається з #!/usr/bin/env php, тому shell також має знаходити 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 тепер запобігає одночасним запускам. Пропущений запуск одразу й без повідомлення завершується за задумом. Проблему одночасних запусків потрібно вирішувати в crontab, а не покладатися на ядро хоста: навіть розміщення завдань із урахуванням кешу, додане в Linux kernel 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 на nginx. - Кешування сторінок не має кешувати
wp-cron.php, інакше cron-запити отримуватимуть кешовану відповідь, а нічого не виконуватиметься. - Ви не отримуєте окремих результатів для кожної події, тому єдиною ознакою виконання завдання буде його результат.
-sS не виводить повідомлень під час успішного виконання, але продовжує виводити помилки. Саме це потрібно для завдання cron.
Що ще варто запланувати на сервері
Після того як системний cron обробляє чергу WordPress, зберігайте інші регулярні завдання сервера в тому самому місці, де їх можна переглядати. Оновлення безпеки операційної системи слід доручити автоматичним оновленням, а не рядку cron, який ви підтримуєте вручну. Оновлення плагінів і тем WordPress потребують окремого рішення: wp plugin update --all у crontab може непомітно зламати робочий сайт о 3:00, тому виконуйте їх свідомо або після етапу перевірки в staging-середовищі та створення резервної копії.
FAQ
Чи припиняє вимкнення WP-Cron публікацію запланованих записів?
Ні, якщо чергу обробляє інший механізм. DISABLE_WP_CRON лише забороняє завантаженню сторінок запускати чергу. Події й надалі плануються так само. Запис, запланований на 09:00, буде опублікований під час першого запуску cron після 09:00, тому інтервал у п’ять хвилин опублікує його не пізніше 09:05. Якщо встановити константу й не додати cron-запис, запис залишатиметься у списку зі статусом Missed schedule, доки щось не запустить обробку черги.
Від імені якого користувача має запускатися завдання WordPress cron?
Від імені користувача, якому належать файли, що їх записує PHP. У типовій інсталяції Ubuntu це www-data. Перевірте це за допомогою stat -c '%U %G' /srv/www/example.com/wp-content/uploads і порівняйте результат із рядком user = у конфігурації пулу PHP-FPM. Запуск завдання від імені root спричиняє завершення WP-CLI з помилкою YIKES, а примусовий запуск через --allow-root залишає файли, власником яких є root, у wp-content. Після цього веб-сервер не може записувати до них.
Як часто system cron має запускати WordPress cron?
Для більшості сайтів достатньо одного запуску кожні п’ять хвилин. Виберіть інтервал відповідно до найкоротшого розкладу, від якого ви справді залежите, і залиште достатній запас понад час одного запуску. Виміряти цей час можна, додавши time перед командою WP-CLI. На завантаженому сайті інтервал в одну хвилину призводить до накладання запусків, якщо їх не захищає flock.
Чому wp cron test завершується помилкою після вимкнення WP-Cron?
Тому що ця команда перевіряє запуск WP-Cron через відвідувача та повідомляє про помилку, коли DISABLE_WP_CRON має значення true. Для сервера з такою конфігурацією це очікуваний результат. Перевірте шлях system 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-процесі без обмеження веб-тайм-аутом і виводить один рядок для кожної події з її тривалістю. Тому журнал точно показує, що саме виконалося і скільки часу це тривало.