SSD Nodes Learn 🎉 VPS desde $4.99/mês
Guias Matt ConnorPor Matt Connor

Desativar o WP-Cron e usar o cron do sistema

O WP-Cron depende de visitas e pode atrasar ou acumular tarefas. Desative-o, configure o cron do sistema com WP-CLI e confirme cada execução.

O que é o wp-cron e por que o cron do sistema o substitui

O WP-Cron é o agendador de tarefas integrado no WordPress e só é executado quando alguém solicita uma página. Nada dentro do WordPress é executado por iniciativa própria. Em cada pedido que não é servido a partir de uma cache, o WordPress lê uma lista de tarefas agendadas e, quando alguma está pendente, faz um segundo pedido HTTP para /wp-cron.php para executar o trabalho. Ao transferir essa tarefa para o cron do sistema, obtém uma execução previsível num horário fixo, quer o site tenha tido mil visitantes nesse minuto, quer não tenha tido nenhum.

Duas linhas executam o trabalho efetivo: uma constante em wp-config.php e uma entrada no crontab. Tudo o resto neste guia aborda o que essas duas linhas não indicam. Qual é o utilizador com que a tarefa deve ser executada, como confirmar que os eventos agendados foram realmente executados e quais são as três formas de a configuração falhar sem apresentar nada no site.

Os exemplos usam /srv/www/example.com como diretório do WordPress e www-data como utilizador do servidor Web. Substitua os caminhos e o utilizador pelos seus em todos os locais.

Como as visitas acionam custos do cron num site movimentado

Cada pedido sem cache paga pela verificação. O WordPress carrega a opção cron, compara os timestamps e, quando há algo pendente, chama spawn_cron(), que envia um pedido loopback sem bloqueio para /wp-cron.php. O visitante não espera pelo resultado. Um worker PHP espera. Num VPS pequeno que executa PHP-FPM com pm.max_children = 5, um job agendado lento ocupa um quinto da capacidade PHP durante todo o tempo necessário. É mais provável que seja acionado durante o minuto de maior tráfego, porque é quando ocorre o maior número de carregamentos de páginas.

O WordPress limita as duplicações. Obtém um lock com duração de WP_CRON_LOCK_TIMEOUT, 60 segundos por predefinição, para que visitantes simultâneos não iniciem cada um uma execução. O lock limita a duplicação. Não retira o trabalho do caminho do pedido.

Conte quantas vezes isto ocorre no seu próprio servidor antes de decidir se é relevante. Cada loopback aparece no access log do servidor Web:

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

O Apache escreve no ficheiro /var/log/apache2/access.log. Um total na ordem dos milhares por dia representa um custo real. É o tipo de valor que deve medir no seu próprio servidor, em vez de o consultar num artigo, tal como faria benchmark de um VPS antes e depois de qualquer outra alteração.

O caching altera o cenário. Se um page cache servir a maioria dos pedidos como HTML estático, o PHP nunca é executado nesses pedidos e a verificação do cron nunca ocorre. Um site movimentado com muito cache começa a comportar-se como o site tranquilo abaixo.

Que visitante acionou o cron e interrompeu um site com pouco tráfego

Sem visitantes, não há cron. Um site que recebe poucas visitas por dia executa as tarefas agendadas poucas vezes por dia, em momentos aleatórios, conforme essas visitas chegam.

Os sintomas são sempre semelhantes. Uma publicação agendada para as 09:00 permanece na lista de publicações com a marca Missed schedule até alguém carregar uma página. Os plugins de backup não executam durante a noite. As verificações de atualizações atrasam, por isso o painel não mostra nada para atualizar enquanto já foi lançada uma atualização de segurança. Os emails de pedidos, avisos de renovação e alertas de expiração são enviados tarde.

Nada disso regista um erro. Na perspetiva do WordPress, a tarefa nunca esteve atrasada, porque nunca foi iniciada.

Etapa 1: desative o acionador de visitantes em wp-config.php

Abra /srv/www/example.com/wp-config.php e adicione a constante:

define( 'DISABLE_WP_CRON', true );

Coloque-a acima da linha que contém /* That's all, stop editing! Happy publishing. */, porque a linha logo abaixo desse comentário requer wp-settings.php, e wp-settings.php é o ponto em que o WordPress associa a verificação do cron a init. Uma constante definida depois desse require é configurada tarde demais para alterar o comportamento, e o ficheiro parece correto enquanto o acionador continua ativo.

Confirme se a linha está onde espera:

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

A constante não impede o agendamento de eventos. Os plugins continuam a adicionar tarefas à fila, exatamente como antes. Ela apenas impede que os carregamentos de páginas executem essa fila. Por isso, a fila deixa de ser executada até concluir a etapa 3.

Ela também não bloqueia pedidos diretos a /wp-cron.php. Qualquer pessoa ainda pode solicitar esse URL, o que normalmente não causa problemas porque o ficheiro apenas executa o que está pendente. Bloqueá-lo na configuração do servidor web é opcional. Se o bloquear, o fallback curl perto do final deste guia também deixa de funcionar.

Etapa 2: instalar o WP-CLI

O WP-CLI é a ferramenta oficial de linha de comandos do WordPress. Precisa do binário PHP de linha de comandos, que é um pacote separado do módulo PHP do servidor Web.

php -v
sudo apt install -y php-cli

Instale o WP-CLI a partir do build phar, conforme recomendado pelo guia de instalação oficial:

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 apresenta o caminho do binário PHP, a versão do PHP e a versão do WP-CLI. Se apresentar os três valores, o phar funciona. Em agosto de 2026, o guia de instalação indica PHP 7.2.24 como versão mínima, e o Ubuntu 24.04 inclui PHP 8.3. Portanto, um servidor atual está muito acima desse requisito. Atualize mais tarde com sudo wp cli update.

Execute o WP-CLI como o utilizador do site, nunca como root:

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

Como root, o WP-CLI recusa-se a iniciar:

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

Sugere --allow-root. Não utilize essa opção neste caso. O motivo está descrito no primeiro modo de falha abaixo.

Note também que sudo -u www-data -i não funciona, porque a shell de login dessa conta é /usr/sbin/nologin e é apresentado This account is currently not available. Passar o comando diretamente a sudo -u ignora a shell de login, por isso o comando é executado corretamente.

Agora confirme que o próprio WordPress reconhece a constante da etapa 1:

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

Isto apresenta bool(true). Um erro fatal sobre uma constante não definida significa que a linha define() não está a ser alcançada. Normalmente, isso acontece porque foi colocada abaixo do require.

Etapa 3: adicione a entrada do cron com o utilizador correto

O utilizador correto é o proprietário dos ficheiros que o PHP escreve. Verifique as duas extremidades:

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

Numa instalação predefinida do Ubuntu, ambos devolvem www-data. Se atribuiu ao site um pool PHP-FPM próprio, com o seu próprio utilizador, que é normalmente o resultado de uma configuração por site numa stack LAMP no Ubuntu 24.04, use esse utilizador em tudo o que se segue.

Crie um diretório de logs no qual esse utilizador possa escrever:

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

Edite o crontab desse utilizador:

sudo crontab -u www-data -e

Adicione uma linha:

*/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

Por partes. */5 executa a tarefa a cada cinco minutos. flock -n obtém um ficheiro de bloqueio e termina imediatamente se uma execução anterior ainda o mantiver bloqueado. /usr/local/bin/wp é o caminho absoluto de que o cron precisa. --path permite executar o comando a partir de qualquer diretório de trabalho. --due-now processa apenas os eventos cuja hora chegou, em vez de processar todos os eventos da fila. O redirecionamento envia a saída normal e os erros para um único ficheiro que pode consultar.

Na prática, esse redirecionamento não é opcional. O cron envia a saída de uma tarefa por correio eletrónico para o respetivo utilizador, a maioria das imagens VPS não tem um agente de transferência de correio instalado, e o cron regista então (CRON) info (No MTA installed, discarding output) e descarta a saída. Um ficheiro preserva essa informação.

Verifique o ficheiro guardado:

sudo crontab -u www-data -l

Para vários sites, use uma linha para cada um, com minutos desfasados para que não iniciem todos ao mesmo tempo:

*/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

O log cresce indefinidamente se não o rodar. Escreva /etc/logrotate.d/wp-cron:

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

Verifique se a sintaxe é válida sem executar nada: sudo logrotate --debug /etc/logrotate.d/wp-cron.

Etapa 4: confirme que os eventos agendados foram realmente executados

Uma linha de crontab guardada com sucesso não prova nada. Comece pela verificação mais simples e avance até à que confirma efetivamente o resultado.

Primeiro, o cron iniciou o comando? O cron escreve no journal através da sua própria unidade:

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

Uma entrada normal tem este aspeto, sem o carimbo de data/hora e o nome do host no início:

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)

Essa linha significa que o cron iniciou o seu comando como www-data. Não indica se o comando foi executado com sucesso.

Segundo, o WordPress executou alguma coisa? Leia o ficheiro de log:

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

O WP-CLI escreve uma linha por evento e, depois, um total:

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

Os erros são escritos no mesmo ficheiro, que é precisamente o objetivo de 2>&1. Na maioria das execuções não haverá eventos pendentes e será escrito muito pouco. Por isso, leia o ficheiro depois de uma execução em que sabe que havia trabalho pendente.

Terceiro, confirme o fluxo completo. Agende um evento marcador e observe quando desaparece:

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

Aguarde um intervalo e execute novamente o comando de listagem. O hook desapareceu porque um evento executado uma única vez é removido da fila quando é executado. Nenhum plugin regista um callback com esse nome de hook, portanto a sua execução não faz mais nada no site. Se o hook continuar listado depois de dois intervalos, a fila não está a ser executada. As duas primeiras verificações indicam se o problema está no cron ou no WP-CLI.

Não use wp cron test para esta verificação. Esse comando verifica se o disparo iniciado por visitantes funciona e gera um erro quando DISABLE_WP_CRON é verdadeiro. Num servidor configurado corretamente, esse erro é o resultado esperado, não uma falha.

A alternativa com timer do systemd

Se o restante dos trabalhos agendados do servidor já for executado como serviços e timers do systemd, coloque o WordPress nesse sistema também. Cada execução aparecerá em systemctl list-timers, e a saída será enviada para o journal, em vez de ser gravada num ficheiro que tenha de rodar.

Escreva /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

Depois /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

O systemd não executa duas cópias do mesmo serviço ao mesmo tempo, por isso esta versão não precisa de flock. Persistent=true faz com que o serviço compense uma execução perdida enquanto a máquina estava desligada, algo que uma entrada do crontab não consegue fazer.

Escolha o crontab ou o timer. Executar ambos no mesmo site significa que a fila será processada duas vezes, e as execuções duplicadas de uma tarefa de email ou de encomendas ficam visíveis para os seus clientes.

Por que o cron job não deve ser executado como root

Esta é a primeira das três formas de falha da configuração. Coloque o job no crontab de root e o WP-CLI para antes de fazer qualquer coisa:

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

A fila nunca é processada e, se não redirecionar a saída, a mensagem não é apresentada. A correção perigosa é adicionar --allow-root, porque todos os ficheiros que um plugin criar durante essa execução pertencerão a root. O pedido web seguinte será executado como www-data, não poderá escrever nesses diretórios e o site começará a apresentar mensagens como:

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

Corrija o proprietário e depois mova o job:

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

O crontab de root e o crontab de www-data são ficheiros separados. Remover a linha de um deles não altera o outro. Verifique ambos:

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

Por que o cron apresenta wp: not found

Esta e a segunda falha. O cron fornece aos jobs dos utilizadores um PATH muito curto, /usr/bin:/bin. O WP-CLI e instalado em /usr/local/bin, que nao faz parte dessa lista. O job inicia, falha numa fracao de segundo e o log fica com uma linha:

/bin/sh: 1: wp: not found

Consulte o ambiente do cron diretamente, em vez de adivinhar. Adicione temporariamente uma linha:

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

Leia /tmp/cron-env.txt depois de um intervalo e remova a linha. O valor de PATH= nesse ficheiro e exatamente o que o seu job recebe.

Ha duas correcoes. Use o caminho absoluto /usr/local/bin/wp, como no passo 3. Ou defina o PATH uma vez no inicio do crontab, antes de todas as linhas de jobs:

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

A mesma armadilha existe um nivel abaixo. O phar wp comeca com #!/usr/bin/env php, portanto a shell tambem tem de conseguir encontrar php. Se o PHP estiver fora de /usr/bin, o que acontece com builds personalizados e builds de paineis de controlo, obtem:

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

Nesse caso, invoque explicitamente o interpretador, por exemplo /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now.

Por que um intervalo de um minuto recria o problema original

Esta é a terceira falha. * * * * * parece mais seguro do que cinco minutos, mas, num site movimentado, coloca-o novamente na situação inicial. Se uma execução demora mais do que o intervalo, a execução seguinte começa enquanto a primeira ainda está em curso. Dez minutos depois, há dez processos PHP, cada um a consumir a sua própria memória e a manter a sua própria ligação à base de dados.

Verifique diretamente se há acumulação:

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

etimes é a idade do processo em segundos. Uma linha indica um estado normal. Várias linhas com idades muito superiores ao seu intervalo indicam que as execuções estão a acumular-se. Num VPS pequeno, isto pode terminar num erro de Too many connections do MySQL ou levar o kernel a terminar processos PHP para libertar memória. Pode confirmar este último caso com sudo dmesg -T | grep -i 'killed process'.

O WP-CLI executa diretamente os callbacks dos eventos, em vez de solicitar wp-cron.php. Por isso, o bloqueio de 60 segundos que o WordPress usa para impedir criações duplicadas não se aplica aqui. flock -n na entrada da etapa 3 é o que impede agora a sobreposição. Uma execução ignorada termina imediatamente e sem emitir mensagens, por definição.

Escolha o intervalo com base no agendamento mais curto de que realmente depende e meça primeiro a duração de uma execução:

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

Cinco minutos é um valor predefinido razoável: uma publicação agendada para as 09:00 é publicada até às 09:05. Quinze minutos é adequado para um site sem tarefas críticas em termos de tempo. Um minuto deve ser usado por lojas e plugins orientados por filas que realmente precisam desse intervalo, e apenas depois de confirmar que uma execução termina em poucos segundos.

Se não conseguir instalar o WP-CLI

Alguns hosts bloqueiam ferramentas de shell. Um pedido HTTP simples para wp-cron.php executa a mesma fila, mas através de toda a stack web:

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

O que perde, de forma clara:

  • A execução é limitada pelos timeouts de pedidos do servidor web e do PHP-FPM, por isso um trabalho longo pode ser interrompido a meio.
  • O certificado tem de ser válido ou curl termina com SSL certificate problem, por isso mantenha as renovações a funcionar com Certbot no nginx.
  • O cache de páginas não pode armazenar wp-cron.php, caso contrário os pedidos do cron recebem uma resposta em cache e nada é executado.
  • Não recebe qualquer saída por evento, por isso a única prova de que um trabalho foi executado é o efeito que produziu.

-sS mantém o curl silencioso em caso de sucesso, mas continua a apresentar erros, que é o comportamento pretendido num cron job.

O que mais deve fazer parte do agendamento do servidor

Depois de o cron do sistema assumir a fila do WordPress, mantenha o restante trabalho de rotina do servidor no mesmo local, onde possa consultá-lo. As atualizações de segurança do sistema operativo devem ser geridas por atualizações automáticas, e não por uma linha de cron mantida manualmente. As atualizações de plugins e temas do WordPress são uma decisão diferente: wp plugin update --all num crontab pode facilmente interromper um site em produção às 3am sem ninguém a monitorizar, por isso execute-as de forma deliberada ou após uma etapa de staging e um backup.

FAQ

A desativação do WP-Cron impede a publicação de artigos agendados?

Não, desde que outro processo execute a fila. DISABLE_WP_CRON apenas impede que os carregamentos de páginas acionem a fila. Os eventos continuam agendados exatamente como antes. Um artigo definido para as 09:00 é publicado na primeira execução do cron depois das 09:00. Portanto, um intervalo de cinco minutos publica-o até às 09:05. Se definir a constante e nunca adicionar a entrada do cron, o artigo permanece na lista com a indicação Missed schedule até que algo execute a fila.

Que utilizador deve executar o cron do WordPress?

O utilizador proprietário dos ficheiros que o PHP escreve, que é www-data numa instalação padrão do Ubuntu. Verifique com stat -c '%U %G' /srv/www/example.com/wp-content/uploads e compare o resultado com a linha user = na configuração do pool PHP-FPM. Executar o job como root faz o WP-CLI parar com um erro YIKES. Forçá-lo com --allow-root deixa ficheiros pertencentes a root dentro de wp-content, que o servidor Web não consegue escrever posteriormente.

Com que frequência o cron do sistema deve executar o cron do WordPress?

A cada cinco minutos é adequado para a maioria dos sites. Ajuste o intervalo ao agendamento mais curto de que realmente depende e mantenha-o confortavelmente acima do tempo necessário para uma execução individual. Pode medir esse tempo colocando time antes do comando do WP-CLI. Intervalos de um minuto sobrepõem execuções num site ocupado, a menos que flock as impeça.

Porque é que wp cron test falha depois de eu desativar o WP-Cron?

Porque esse comando testa a criação de processos acionada pelos visitantes e comunica um erro quando DISABLE_WP_CRON está definido como true. Esse é o resultado correto num servidor configurado desta forma. Verifique o caminho do cron do sistema. Leia /var/log/wp-cron/example.log ou agende um evento marcador com wp cron event schedule e confirme que desapareceu de wp cron event list depois da execução seguinte.

Preciso do WP-CLI ou o curl para wp-cron.php é suficiente?

O curl funciona e é a opção correta quando não pode instalar o WP-CLI. É mais lento porque carrega o WordPress através do servidor Web e está limitado pelo tempo limite do pedido. O WP-CLI executa os eventos num processo PHP de linha de comandos sem tempo limite Web. Também apresenta uma linha por evento com a respetiva duração, para que o log indique exatamente o que foi executado e quanto tempo demorou.

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