Post agendado no WordPress e o disparo nas redes sociais

Notebook aberto com a tela apagada sobre mesa de madeira clara, celular apoiado em um suporte de madeira ao lado, luz de janela vindo da esquerda

Este blog publicou 27 artigos desde 19 de agosto. Dezenove deles saíram na mesma janela da noite e nenhum saiu no mesmo segundo: o mais adiantado entrou às 19:18:27, o mais atrasado às 19:25:43. São 7 minutos e 16 segundos de espalhamento, distribuídos em sete minutos diferentes do relógio, para uma tarefa que tem hora marcada e roda sozinha todo dia.

Ninguém mexeu na configuração entre um dia e outro. A hora marcada é um pedido; a execução é um segundo evento, e ele acontece quando a máquina chega nele. Quem agenda post no WordPress convive com a mesma distância, com um detalhe que muda a escala do problema: ali o que empurra a fila não é um relógio, é uma visita ao site. E quando a publicação é o primeiro elo de uma esteira que ainda tem que postar no Instagram e avisar a equipe, o atraso não termina no atraso.

O que os 19 carimbos medem, e o que não medem

Os artigos daqui não passam pelo agendador do WordPress. Uma rotina diária escreve direto na REST API com o status já em publish, então o horário gravado é o instante em que a rotina conseguiu rodar. O espalhamento de sete minutos é do agendador que chama a rotina, não do WP-Cron.

O número serve como piso. Aquele agendador é um cron de verdade: roda em máquina dedicada, não depende de tráfego nenhum, ninguém disputa o horário com ele. Mesmo assim variou sete minutos em dezenove tentativas. O WP-Cron trabalha em condições bem piores, e a variação dele não se mede em minutos, se mede em quando alguém abre uma página do seu site.

Tem um caso mais gritante nesses 27 artigos: um deles não caiu na janela da noite. Saiu às 9:17 da manhã, dez horas antes do horário dos outros dezenove. Não há como saber o que aconteceu naquele dia, e é exatamente esse o ponto. Não existe registro de tentativa, de atraso nem de recuperação em lugar nenhum. Sobrou um carimbo de horário fora do padrão, que só apareceu porque alguém foi contar. É assim que agendamento quebrado se parece por dentro: não como erro, como uma data diferente da combinada.

Onde o WordPress guarda a hora que você marcou

Quando você salva um artigo com data futura, o status dele vira future e o WordPress cria um evento único de cron para aquele post específico. A função responsável é _future_post_hook(), e ela faz duas coisas na ordem: limpa qualquer agendamento anterior daquele post e agenda um novo evento publish_future_post para o timestamp GMT da data que você escolheu.

Na hora marcada, quem atende é check_and_publish_future_post(). Ela confere se o post ainda está em future, compara a data com o momento atual e decide: se o horário ainda não chegou, reagenda o evento e sai sem publicar; se já passou, chama wp_publish_post() e o artigo vai ao ar.

Repare que não existe nenhuma verificação de atraso nessa comparação. O código pergunta se a hora já passou, não quanto tempo faz que passou. Um evento que deveria ter rodado às 9h e só roda às 14h publica o artigo às 14h sem reclamar, e é por isso que a maioria dos atrasos de agendamento passa em branco: do ponto de vista do WordPress, aquilo funcionou.

Quem aperta o gatilho é a visita, não o relógio

O evento está agendado, a função está pronta, e nada disso roda por conta própria. A documentação do WordPress é direta sobre o mecanismo: “WP-Cron works by checking, on every page load, a list of scheduled tasks to see what needs to be run.” É uma checagem por carregamento de página.

Na prática, a função wp_cron() pendura a execução na ação shutdown da requisição. Ou seja: alguém precisa abrir uma página, o PHP precisa processar essa página até o fim, e só então a fila de tarefas é olhada. Sem requisição, a fila fica intacta por quanto tempo for.

A própria documentação dá o exemplo em que isso dói: “Scheduling errors could occur if you schedule a task for 2:00PM and no page loads occur until 5:00PM.” Três horas de atraso, sem erro, sem aviso. Em blog novo, em blog de nicho, em site institucional que recebe visita em horário comercial e silêncio de madrugada, agendar para 6h da manhã é agendar para o primeiro visitante depois das 6h.

Esse é o comportamento padrão, e ele tem remédio oficial. A documentação recomenda desligar o gatilho por visita com define( 'DISABLE_WP_CRON', true ); no wp-config.php e chamar o arquivo de fora, por um cron de sistema, com uma linha do tipo wget --delete-after http://SEU_SITE/wp-cron.php. A partir daí a fila passa a ser olhada em intervalo fixo, independente de quem entrou no site.

O cache que impede o PHP de acordar

Existe um caso pior que o site sem tráfego: o site com tráfego cujas visitas não acordam o PHP. Cache de página entrega HTML pronto do disco ou da memória, sem executar o WordPress. A visita acontece, o visitante é servido, e a ação shutdown do WordPress nunca é alcançada porque o WordPress não rodou.

No fórum oficial existe o relato inteiro desse cenário. O autor do tópico descreve o sintoma com as palavras de quem só vê a tela: “The time/date passes, and I see a red ‘missed schedule’ error.” Nenhum plugin novo, nenhuma mudança de hospedagem, agendamento que funcionava e parou. O suporte pediu para desativar o plugin de cache, o post de teste saiu na hora, e a investigação seguiu para descobrir qual configuração específica de cache de página estava servindo tudo estático.

É um diagnóstico que quase nunca é o primeiro palpite, porque cache e agendamento parecem assuntos de departamentos diferentes. São o mesmo assunto quando o agendador depende de execução de PHP.

O post sai e o envio à rede fica para trás

Até aqui o problema era o artigo ir ao ar. Agora entra o elo seguinte, e ele tem uma falha própria.

Quando o artigo agendado finalmente vira público, wp_publish_post() chama wp_transition_post_status(), que dispara a ação transition_post_status com o status novo, o status antigo e o objeto do post. Um plugin que escuta essa troca recebe o post agendado igual ao post publicado à mão: para ele, a origem do publish não faz diferença. Essa é a razão técnica de o agendamento do WordPress e a publicação automática na rede conseguirem conviver sem gambiarra.

O problema é que o envio à rede raramente acontece dentro dessa mesma requisição, e não deveria: postar no Instagram são duas chamadas HTTP à Graph API, uma para criar o contêiner de mídia em /media e outra para publicar em /media_publish. Segurar o salvamento do post esperando resposta da Meta é pedir para o editor travar. O caminho sensato é agendar o envio para segundos depois, e aí a esteira volta a depender do cron que já se mostrou frágil.

Pior: o wp-cron.php remove o evento da fila antes de executá-lo. Na ordem exata do arquivo, o timestamp é conferido, o evento recorrente é reagendado, wp_unschedule_event() é chamado, e só então do_action_ref_array() roda o gancho. Se o PHP estourar o tempo no meio do envio, o evento já não existe mais. O artigo está publicado, o registro diz que o envio está pendente, e não há nada agendado para chamá-lo de volta.

O que o atraso faz com a janela da Meta

Na maior parte dos casos, atraso de publicação é só um problema de pontualidade. Há dois pontos em que ele passa a quebrar coisa.

O primeiro é o contêiner de mídia. Criar o contêiner e publicá-lo são etapas separadas, e a documentação de publicação de conteúdo do Instagram avisa que o contêiner não publicado em 24 horas expira. Uma esteira que cria o contêiner na hora do publish e tenta publicar depois, num segundo evento de cron que ficou preso, tem esse teto para acordar. Passou de 24 horas, o contêiner morreu e o envio precisa começar de novo.

O segundo é a fila que se acumula. A conta do Instagram aceita 100 publicações por API dentro de uma janela móvel de 24 horas, com carrossel contando como uma. Cem é bastante para um blog; deixa de ser bastante quando quatro dias de agendamento atrasado destravam juntos porque alguém finalmente visitou o site, e a agência que cuida de vários perfis sente isso antes. A janela é móvel, então não zera à meia-noite: ela zera 24 horas depois de cada envio.

Fora da técnica sobra o efeito mais óbvio e o menos discutido. Artigo agendado para as 9h que sai às 14h perde a razão de ter sido agendado para as 9h, e o post na rede sai no horário errado junto. Quem agenda para ganhar horário está comprando exatamente aquilo que o WP-Cron não garante, e esse custo tem uma conta própria que já detalhamos em agendar post no Instagram e o custo que volta toda semana.

A rede de segurança precisa existir nos dois lados

Do lado do WordPress, o remédio é o cron de sistema descrito acima. Ele resolve o atraso de publicação e não resolve o envio que morreu no meio, porque esse envio não está mais agendado para ninguém encontrar.

Do lado do envio, o que funciona é alguém revisitar o estado depois. No Post Ping isso é uma verificação diária que procura artigos publicados que não foram notificados e trata cada um como pendência aberta, em vez de confiar que o evento de cron original deu conta. A detecção da publicação é a mesma descrita acima, pela troca de status, então o artigo agendado entra na esteira sem nenhum passo extra na redação. As duas coisas estão descritas em recursos.

Nenhuma das duas peças substitui a outra. Cron de sistema sem repescagem publica na hora e perde envios; repescagem sem cron de sistema envia tudo, sempre tarde.

Por que você não consegue auditar o atraso depois

Aqui está a parte que surpreende quem vai procurar evidência: ela não existe. A função wp_publish_post() faz um único UPDATE na tabela de posts, e esse update grava um campo só, o post_status. A data do post continua sendo a data que você marcou. O post_modified não é tocado.

Então um artigo agendado para as 9h e publicado de fato às 14h fica registrado como publicado às 9h, para sempre, em todos os lugares que leem esse campo: a lista de posts, o feed, o sitemap, a REST API. Se o agendamento do WordPress fosse o que publica este blog, os 27 carimbos do começo deste artigo não serviriam para medir atraso nenhum: eles marcariam o pedido, não a entrega.

Quem quer o número real precisa medir do lado de fora. O registro de envio às redes tem a hora verdadeira, porque é gravado quando a chamada acontece. Comparar essa hora com a data do post é a única forma de saber se o agendamento está cumprindo o horário, e é uma comparação que vale fazer uma vez por mês em blog que agenda.

Se o ponto de partida for antes disso, e a dúvida for como a publicação sai do WordPress e chega às redes sem ninguém apertar botão, o caminho inteiro está em publicar do WordPress no Instagram e Facebook automaticamente.

Este blog publica sozinho nas redes

Cada artigo daqui vira post no Instagram e no Facebook automaticamente: legenda escrita por IA, card visual gerado na hora. Quem faz isso é o Post Ping, o mesmo plugin que você pode instalar no seu WordPress.

Ver planos do Post Ping