Desativar o wp-cron e usar o cron do sistema
O WP-Cron depende de visitas e falha em sites parados. Desative-o, agende o wp-cli no cron do sistema e confirme a execução das tarefas.
O que é o wp-cron e por que o cron do sistema o substitui
O WP-Cron é o agendador de tarefas integrado no WordPress. Ele só é executado quando alguém pede 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. Se alguma estiver pendente, envia um segundo pedido HTTP para si próprio em /wp-cron.php para executar a tarefa. Transferir essa tarefa para o cron do sistema garante uma execução previsível num horário fixo, independentemente de o site ter tido mil visitantes nesse minuto ou nenhum.
Duas linhas executam o trabalho: uma constante em wp-config.php e uma entrada no crontab. Tudo o resto neste guia explica o que essas duas linhas não indicam. É necessário saber com que utilizador 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 qualquer mensagem no site.
Os exemplos usam /srv/www/example.com como diretório do WordPress e www-data como utilizador do servidor Web. Substitua sempre estes caminhos e utilizador pelos seus próprios valores.
Como uma visita aciona 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á uma tarefa vencida, 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 do PHP durante todo o tempo necessário. É mais provável que seja acionado durante o minuto de maior movimento, porque é quando ocorrem mais carregamentos de páginas.
O WordPress limita as duplicações. Obtém um lock com duração de WP_CRON_LOCK_TIMEOUT, de 60 segundos por predefinição, para que visitantes simultâneos não iniciem uma execução cada um. O lock limita a duplicação. Não retira o trabalho do caminho do pedido.
Conte com que frequência isso ocorre no seu próprio servidor antes de decidir se é relevante. Cada pedido loopback aparece no access log do servidor web:
sudo grep -c 'wp-cron.php' /var/log/nginx/access.logO Apache escreve em /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 obter num artigo, tal como faria ao comparar o desempenho de um VPS antes e depois de qualquer outra alteração.
O cache 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 silencioso abaixo.
Como uma visita desencadeia falhas do cron num site com pouco tráfego
Sem visitas, não há cron. Um site que recebe poucas visitas por dia executa as tarefas agendadas poucas vezes por dia, nos momentos aleatórios em que essas visitas ocorrem.
Os sintomas são sempre semelhantes. Um artigo agendado para as 09:00 permanece na lista de artigos com a marca Missed schedule até alguém carregar uma página. Os plugins de backup não executam o backup durante a noite. As verificações de atualizações atrasam-se, por isso o painel não mostra atualizações disponíveis quando já foi lançada uma atualização de segurança. Os emails de encomendas, os avisos de renovação e os alertas de expiração são enviados tarde.
Nada disto regista um erro. Na perspetiva do WordPress, a tarefa nunca esteve atrasada, porque nunca foi iniciada.
Passo 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 é onde o WordPress associa a verificação do cron a init. Uma constante definida depois desse require é configurada tarde demais para alterar qualquer coisa. O ficheiro parece correto, mas o acionador continua ativo.
Confirme se a linha está no local esperado:
grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.phpA 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 processem essa fila. Assim, a fila deixa de ser processada até concluir o passo 3.
Ela também não bloqueia pedidos diretos a /wp-cron.php. Qualquer pessoa pode continuar a pedir esse URL. Normalmente, isso não causa problemas, porque o ficheiro apenas executa as tarefas que estão pendentes. 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.
Passo 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-cliInstale o WP-CLI a partir da compilação phar, conforme recomendado pelo guia oficial de instalação:
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 mostra o caminho do binário PHP, a versão do PHP e a versão do WP-CLI. Se mostrar 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 versionComo root, o WP-CLI recusa iniciar:
Error: YIKES! It looks like you're running this as root.Sugere --allow-root. Não utilize essa opção aqui. A razão é explicada no primeiro modo de falha abaixo.
Tenha também em atenção que sudo -u www-data -i não funciona, porque a shell de início de sessão dessa conta é /usr/sbin/nologin e obtém This account is currently not available.. Passar o comando diretamente para sudo -u ignora a shell de início de sessão, pelo que o comando é executado corretamente.
Agora confirme que o próprio WordPress reconhece a constante do passo 1:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'Isto mostra bool(true). Um erro fatal sobre uma constante indefinida significa que a linha define() não foi alcançada. Normalmente, isso acontece porque ficou depois de 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.confNuma instalação padrão do Ubuntu, ambas respondem 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 em uma 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-cronEdite o crontab desse utilizador:
sudo crontab -u www-data -eAdicione 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>&1Por partes. */5 executa o comando a cada cinco minutos. flock -n obtém um ficheiro de bloqueio e termina imediatamente se uma execução anterior ainda o tiver bloqueado. /usr/local/bin/wp é o caminho absoluto, necessário para o cron. --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 email para o respetivo utilizador, a maioria das imagens de VPS não tem um agente de transferência de email instalado e, por isso, o cron regista (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 -lPara vários sites, use uma linha para cada um, com minutos desfasados para que não sejam todos iniciados 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>&1O 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 alterar 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 wpUma entrada normal tem este aspeto, depois de remover o timestamp 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.logO WP-CLI escreve uma linha por evento e, no fim, um total:
Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.Os erros são registados no mesmo ficheiro, que é precisamente a finalidade 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 saiba que havia trabalho pendente.
Terceiro, confirme o funcionamento de ponta a ponta. 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_checkAguarde um intervalo e execute novamente o comando de listagem. O hook desapareceu porque um evento único é removido da fila quando é executado. Nenhum plugin regista um callback com esse nome de hook, por isso executá-lo não faz mais nada no site. Se o hook continuar listado depois de dois intervalos, a fila não está a ser processada. As duas primeiras verificações indicam se o problema está no cron ou no WP-CLI.
Não use wp cron test para isto. Esse comando verifica se o spawning acionado 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 o temporizador do systemd
Se as restantes tarefas agendadas do sistema já forem executadas como serviços e temporizadores do systemd, coloque também o WordPress nesse sistema. Cada execução aparece em systemctl list-timers, e a saída é enviada para o journal, em vez de para um ficheiro que tenha de ser submetido a rotação.
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-nowDepois, /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 20O systemd não executa duas instâncias do mesmo serviço ao mesmo tempo, por isso esta versão não precisa de flock. Persistent=true faz com que execute uma tarefa perdida enquanto a máquina estava desligada, algo que uma entrada do crontab não consegue fazer.
Escolha o crontab ou o temporizador. Executar ambos no mesmo site significa que a fila é 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. Se você colocar o job no crontab do root, o WP-CLI para antes de fazer qualquer coisa:
Error: YIKES! It looks like you're running this as root.A fila nunca é executada e, se você não redirecionar a saída, nunca verá a mensagem. A correção perigosa é adicionar --allow-root, porque então todos os arquivos gravados por um plugin durante essa execução pertencerão ao root. A próxima solicitação web será executada como www-data, não poderá gravar nesses diretórios e o site começará a exibir 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-contentO crontab do root e o crontab de www-data são arquivos separados. Portanto, remover a linha de um deles não altera o outro. Verifique ambos:
sudo crontab -u root -l
sudo crontab -u www-data -lPor que o cron informa wp: not found
Esta é a segunda falha. O cron fornece aos jobs dos utilizadores um PATH muito curto, /usr/bin:/bin. O WP-CLI é instalado em /usr/local/bin, que não está nessa lista. O job inicia, falha numa fração de segundo e o log contém uma linha:
/bin/sh: 1: wp: not foundVeja o ambiente do cron diretamente, em vez de fazer suposições. Adicione uma linha temporária:
*/5 * * * * env > /tmp/cron-env.txt 2>&1Leia /tmp/cron-env.txt depois de um intervalo e remova a linha. O valor de PATH= nesse ficheiro é exatamente o ambiente recebido pelo job.
Há duas correções. Use o caminho absoluto /usr/local/bin/wp, como no passo 3. Ou defina o PATH uma vez no início do crontab, antes de todas as linhas de jobs:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binO mesmo problema ocorre um nível abaixo. O phar wp começa com #!/usr/bin/env php, portanto a shell também precisa de encontrar php. Se o PHP estiver fora de /usr/bin, algo que ocorre com builds personalizados e builds de painéis de controlo, verá:
/usr/bin/env: 'php': No such file or directoryNesse caso, chame 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, existem dez processos PHP, cada um a ocupar a sua própria memória e a sua própria ligação à base de dados.
Procure diretamente uma acumulação de processos:
ps -eo etimes,user,args | grep '[c]ron event run'etimes é a idade do processo em segundos. Uma linha é 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 acaba num erro de Too many connections do MySQL ou o kernel termina processos PHP para libertar memória. Pode confirmar esta situação 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 a criação de processos duplicados não se aplica aqui. flock -n na entrada do passo 3 é o que impede agora a sobreposição. Uma execução ignorada termina imediatamente e sem emitir mensagens, por definição. A sobreposição é um problema que tem de resolver no crontab, e não um problema que o kernel do host resolva por si. Até a colocação de tarefas com reconhecimento de cache adicionada no kernel Linux 7.2 apenas decide em que núcleo um processo é executado, nunca quantos processos iniciou.
Escolha o intervalo com base no agendamento mais curto de que realmente depende e meça primeiro uma execução:
time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-nowCinco minutos é um valor predefinido sensato: uma publicação agendada para as 09:00 fica publicada até às 09:05. Quinze minutos é adequado para um site sem conteúdo crítico em termos de tempo. Um minuto é indicado para lojas e plugins orientados por filas que realmente precisam dessa frequência, e apenas depois de confirmar que uma execução termina em poucos segundos.
Se não puder instalar o WP-CLI
Alguns hosts bloqueiam ferramentas de shell. Uma solicitação HTTP simples para wp-cron.php executa a mesma fila, mas passa por toda a pilha Web:
*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/nullO que perde, de forma clara:
- A execução fica limitada pelos tempos limite 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
curltermina comSSL certificate problem. Mantenha as renovações a funcionar com Certbot no nginx. - O cache de páginas não pode armazenar
wp-cron.phpem cache, caso contrário as solicitações do cron recebem uma resposta em cache e nada é executado. - Não obtém resultados por evento. A única evidência de que um trabalho foi executado é o efeito produzido.
-sS mantém o curl silencioso quando a execução é bem-sucedida, mas continua a mostrar erros. É isso que pretende num trabalho do cron.
O que mais deve fazer parte da agenda do servidor
Depois de o cron do sistema assumir a fila do WordPress, mantenha no mesmo local o restante trabalho rotineiro do servidor, 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 depois de uma etapa de staging e de um backup.
FAQ
A desativação do WP-Cron impede a publicação de posts agendados?
Não, desde que outra ferramenta execute a fila. DISABLE_WP_CRON apenas impede que o carregamento de páginas acione a fila. Os eventos continuam agendados exatamente como antes. Um post 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 post permanece na lista marcado como Missed schedule até que algo execute a fila.
Que utilizador deve executar o cron job do WordPress?
O utilizador que é proprietário dos ficheiros escritos pelo PHP, que é www-data numa instalação Ubuntu predefinida. 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 do 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 de uma única execução. 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.
Por 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 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 e imprime uma linha por evento com a respetiva duração. Assim, o log mostra exatamente o que foi executado e quanto tempo demorou.