Як вимкнути wp-cron і налаштувати системний cron
WP-Cron запускається лише під час відкриття сторінки: на тихих сайтах зависає, на завантажених накопичує завдання. Перенесіть запуск у system 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 як користувача веб-сервера. Усюди замініть їх на власні шляхи та ім’я користувача.
Який відвідувач спричинив витрати WP-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 секунд, тому одночасні відвідувачі не запускають окремий процес кожен. Блокування обмежує дублювання. Воно не переносить виконання за межі шляху обробки запиту.
Перш ніж вирішувати, чи має це значення, перевірте частоту запусків на власному сервері. Кожен loopback-запит з’являється в журналі доступу веб-сервера:
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 binary командного рядка. Це окремий пакет, не 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 binary, версію PHP і версію WP-CLI. Якщо виведено всі три значення, phar працює. Станом на August 2026 посібник зі встановлення вказує PHP 7.2.24 як мінімальну версію. Ubuntu 24.04 постачається з PHP 8.3, тому актуальний сервер має значний запас щодо цієї вимоги. Пізніше оновіть WP-CLI за допомогою 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 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-файл і негайно завершує роботу, якщо попередній запуск досі утримує блокування. /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 записує повідомлення до journal у власному 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 зникне, оскільки одноразова подія видаляється з черги після виконання. Жоден plugin не реєструє callback для цього імені hook, тому його запуск більше нічого не змінює на сайті. Якщо hook залишається у списку після двох інтервалів, черга не обробляється. Перші дві перевірки покажуть, проблема в cron чи у WP-CLI.
Не використовуйте для цього wp cron test. Ця команда перевіряє, чи працює запуск процесу за запитом відвідувача, і повертає помилку, коли істинне DISABLE_WP_CRON. На правильно налаштованому сервері ця помилка є очікуваним результатом, а не несправністю.
Альтернатива на основі таймера systemd
Якщо інші заплановані завдання на сервері вже виконуються через сервіси й таймери 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 або таймер. Якщо запустити обидва варіанти для одного сайту, черга оброблятиметься двічі, а повторні запуски завдання для надсилання електронних листів або обробки замовлень будуть помітні вашим клієнтам.
Чому cron job не має запускатися від root
Це перший із трьох способів, через які налаштування не працює. Якщо додати job до 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?Виправте власника файлів, а потім перенесіть 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, що трапляється у custom builds і збірках панелей керування, ви отримаєте:
/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
Деякі хостинги блокують shell-інструменти. Звичайний 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 не виводить нічого в разі успішного виконання curl, але все одно показує помилки. Саме це потрібно для cron-завдання.
Що ще має виконуватися за розкладом на сервері
Після того як системний cron обробляє чергу WordPress, зберігайте інші регулярні завдання сервера в тому самому місці, де їх зручно контролювати. Патчі безпеки операційної системи належать до автоматичних оновлень, а не до рядка cron, який ви підтримуєте вручну. Оновлення плагінів і тем WordPress потребують окремого рішення: wp plugin update --all у crontab може непомітно зламати робочий сайт о 3am, тому запускайте це навмисно або після етапу тестування в 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 залишає файли, власником яких є root, у wp-content. Після цього вебсервер не може записувати до цих файлів.
Як часто system cron має запускати WordPress cron?
Для більшості сайтів достатньо запуску кожні п’ять хвилин. Вибирайте інтервал відповідно до найкоротшого розкладу, від якого ви справді залежите, і залишайте достатній запас порівняно з тривалістю одного запуску. Її можна виміряти, додавши time перед командою WP-CLI. На завантаженому сайті інтервал в одну хвилину спричиняє накладання запусків один на один, якщо їх не захищає flock.
Чому wp cron test завершується помилкою після вимкнення WP-Cron?
Тому що ця команда перевіряє запуск 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 без тайм-ауту вебзапиту та виводить по одному рядку для кожної події із зазначенням її тривалості. Тому журнал точно показує, що було виконано і скільки часу це тривало.