Headless WordPress, ISR ou SSR: escolher o modo de renderização pelo ritmo do conteúdo
A pergunta “ISR ou SSR” só faz sentido rota a rota. Não existe uma resposta para o site inteiro. O Astro e o Next.js permitem escolher o modo ao nível da página ou do layout, e a abordagem de engenharia madura é decidir de forma deliberada, rota a rota, com base num modelo de quantas vezes o conteúdo muda.
Este artigo faz parte do pilar do serviço headless WordPress e complementa a matriz de decisão Next.js ou Astro, que trata da escolha ao nível do framework.
Em resumo
- O ISR (ou estático com revalidação) ganha quando o ritmo de alteração do conteúdo é previsível e o tráfego é elevado.
- O SSR ganha quando a página é personalizada, depende da sessão ou contém dados em tempo real.
- A correção do ISR depende da invalidação da cache; os webhooks são melhores do que a revalidação temporal.
- O Cloudflare Workers pode servir os dois através do adaptador do framework; uma página em cache quase não gasta CPU, o SSR paga a renderização completa.
- O ponto de partida é o modo mais barato que garante um resultado correto; passe para SSR apenas quando for necessário.
SSG, ISR e SSR explicados para WordPress
Geração estática de sites (SSG). A página é construída uma vez, no momento do build, e servida como HTML simples. A mais barata no momento do pedido, a mais lenta a atualizar.
Incremental Static Regeneration (ISR). A página é construída uma vez, mas pode ser regenerada perante um gatilho, normalmente um webhook na publicação ou um intervalo de revalidação temporal. Barata no momento do pedido, com consistência eventual nas atualizações.
Server-Side Rendering (SSR). A página é renderizada a cada pedido. Sempre atual, mas o custo de execução cresce com o tráfego. Personalização, autenticação e dados em tempo real encaixam aqui de forma natural.
No WordPress headless, os três modos leem da origem WordPress através de REST ou GraphQL. A diferença está no momento em que leem.
Quando usar ISR e quando usar SSR no WordPress headless
Contam dois fatores:
Ritmo de alteração do conteúdo. Com que frequência esta página muda? Uma vez por trimestre, uma vez por dia, a cada minuto, em tempo real?
Superfície de personalização. A página muda consoante o visitante? Estado de sessão iniciada, preços consoante a localização, variante de um teste A/B.
A regra: escolha o modo mais barato que garante um resultado correto. O estático é o mais barato. O SSR é o mais caro. Avance para SSR apenas quando um modo mais barato não der um resultado correto.
| Tipo de página | Modo por omissão | Porquê |
|---|---|---|
| Páginas de marketing, artigos do blogue | Estático (reconstrução na publicação) | Poucas alterações, sem personalização |
| Arquivos de categorias e etiquetas | ISR com webhook de publicação | Ritmo ligado à publicação de conteúdo |
| Páginas de produto, catálogo estável | ISR com webhook de stock | Invalidação previsível |
| Páginas de produto, stock em tempo real | SSR com cache na edge | O stock muda em segundos |
| Carrinho e checkout | SSR | Dependem da sessão por definição |
| Painel com sessão iniciada | SSR | Estado por utilizador |
| Página inicial editorial | ISR com webhook de publicação | Ritmo ligado aos eventos de publicação |
Invalidação de cache ISR com webhooks do WordPress
O ISR parece gratuito até servir um URL canónico desatualizado depois de uma alteração de slug. O padrão que o evita:
Invalidação orientada por webhooks. A origem WordPress envia um webhook na publicação, na alteração de slug ou na eliminação de um artigo. O framework de front-end recebe o webhook e dispara a regeneração das páginas afetadas. O custo é uma integração de webhook na origem WordPress, paga uma vez.
Revalidação temporal apenas como rede de segurança. Um intervalo de revalidação de 60 segundos cobre falhas na entrega de webhooks, mas não deve ser o gatilho principal. Um intervalo de 60 segundos não reconstrói cada página 60 vezes por hora: o Next.js só regenera uma página quando chega um pedido depois de a janela expirar. O custo real é que cada página popular pode ser regenerada em segundo plano até uma vez por minuto e, em plataformas que faturam por pedido, esse trabalho em segundo plano conta como computação adicional.
Etiquetas de cache, não URLs. Cada página em cache recebe etiquetas com o ID do artigo no WordPress, os IDs dos termos que referencia e etiquetas transversais (página inicial, sitemap). Quando chega um webhook, o front-end limpa a cache por etiqueta, não por URL. É a diferença entre “regenerar a página do produto” (frágil) e “regenerar tudo o que referencia o produto 8421” (correto).
Custo de ISR e SSR no Cloudflare Workers
O Astro chega ao Workers através do seu adaptador oficial para Cloudflare. O Next.js documenta o ISR para o runtime Node.js e descreve o suporte por adaptadores como dependente da plataforma, por isso verifique o adaptador antes de planear ISR no Workers. O custo modo a modo:
- Estático na edge. O Cloudflare Pages serve HTML simples com quase nenhum CPU por pedido. O modo mais barato.
- ISR. O primeiro pedido após a invalidação paga o custo total de renderização; os pedidos em cache quase nada. No Workers, isto depende da cache do adaptador e não apenas do framework.
- SSR. Cada pedido paga o custo total de renderização no Workers. Previsível por pedido, caro em escala.
A diferença de custo pesa com tráfego elevado. Com pouco tráfego, decide a correção, não o custo.
Exemplos de ISR e SSR em rotas reais do WordPress
Página inicial de marketing. Estática, reconstruída por webhook a cada publicação editorial. Cache de 24 horas na edge com possibilidade de limpeza manual. SSR como alternativa apenas se for acrescentado um banner específico por país.
Página de produto no WooCommerce. ISR com o ID do produto como chave. Webhook do WooCommerce em alterações de stock, de preço ou de conteúdo. Janela de cache: 1 hora como rede de segurança. SSR apenas se mostrar o stock em tempo real for um requisito de UX.
Histórico de encomendas do cliente. SSR. Por utilizador, dependente da sessão, sem cache na edge.
A mesma arquitetura, três modos de renderização diferentes, uma regra de decisão.
Como funciona o revalidate do Next.js em páginas do WordPress
Uma rota com export const revalidate = 3600 serve o HTML em cache a todos os pedidos dentro da janela. O guia de ISR do Next.js atual descreve o que acontece a seguir: o primeiro pedido depois de a janela expirar ainda recebe a página desatualizada, esse pedido inicia uma regeneração em segundo plano e, quando esta termina com sucesso, o pedido seguinte recebe a versão nova. Um artigo que ninguém visita nunca é regenerado. O intervalo limita a frequência com que uma página pode ser reconstruída; não agenda reconstruções.
Quatro pormenores do mesmo guia decidem se isto funciona num front-end WordPress:
- Quando uma rota faz várias chamadas fetch com valores de
revalidatediferentes, o mais baixo define o intervalo de ISR da página. Uma barra lateral que carrega os artigos mais recentes com uma janela de 60 segundos arrasta o artigo inteiro para 60 segundos. - Uma única chamada fetch com
revalidate: 0oucache: 'no-store'torna a rota dinâmica. Uma só chamada sem cache a um endpoint do WordPress, como um contador de visualizações, passa a página de ISR para SSR sem aviso. - Se a regeneração falhar, por exemplo porque a origem WordPress devolve um erro, o Next.js continua a servir a última versão correta e volta a tentar no pedido seguinte. Uma falha da origem não deixa o front-end em branco.
- O guia recomenda um intervalo longo, uma hora em vez de um segundo, e revalidação a pedido quando é precisa precisão. É a regra de webhook primeiro da secção anterior.
O cabeçalho de resposta x-nextjs-cache indica o estado de cada pedido como HIT, STALE, MISS ou REVALIDATED. Lê-lo logo a seguir a uma publicação é a forma mais rápida de confirmar que um webhook chegou ao front-end.
Como revalidar páginas do Next.js com um webhook do WordPress
O lado recetor é um Route Handler, por exemplo app/api/revalidate/route.ts, que verifica um segredo partilhado e chama uma de duas funções de next/cache.
revalidateTag(tag, 'max') marca como desatualizada cada chamada fetch em cache com essa etiqueta, em todas as páginas que a usam (referência de revalidateTag). Com o perfil 'max', os visitantes continuam a receber a cópia antiga enquanto a nova é carregada. Para uma publicação no WordPress, este é normalmente o compromisso certo: as chamadas fetch levam as etiquetas post-8421 e posts, e um só webhook atualiza o artigo, o arquivo e a página inicial. Quando a cópia antiga nunca mais pode ser servida, como num artigo retirado, a referência indica a quem chama a partir de um webhook que passe { expire: 0 }, porque updateTag só funciona dentro de Server Actions. A forma com um único argumento, revalidateTag(tag), está obsoleta. As etiquetas distinguem maiúsculas de minúsculas e têm um limite de 256 caracteres.
revalidatePath(path) atua sobre rotas em vez de dados (referência de revalidatePath). Um caminho literal como /blog/my-post atualiza uma página; um padrão como /blog/[slug] exige o segundo argumento, 'page' ou 'layout'. No WordPress headless há duas armadilhas frequentes. Com rewrites em next.config.js, passe o caminho de destino e não o URL que o visitante vê. E o Proxy não corre nos pedidos de ISR a pedido, por isso um caminho reescrito aí tem de ser revalidado na sua forma final.
As duas funções apenas marcam entradas; a regeneração acontece na visita seguinte. Quando a aplicação corre em várias instâncias com a cache de sistema de ficheiros por omissão, um webhook invalida apenas a instância que o recebeu. A revalidação a pedido precisa então de um cache handler partilhado antes de se poder confiar nela.
Como fazer ISR no Astro
O Astro pré-renderiza todas as páginas por omissão. Uma rota passa à renderização a pedido com export const prerender = false, o que exige um adaptador de servidor. output: 'server' inverte o valor por omissão, e a documentação aconselha manter 'static' até que a maioria das páginas precise mesmo de um servidor (renderização a pedido no Astro). Uma página renderizada a pedido pode definir o seu próprio cabeçalho Cache-Control através de Astro.response.headers.
O Astro 7 acrescenta route caching, que fecha a maior parte da distância para o ISR do Next.js (route caching no Astro). Uma página renderizada a pedido chama Astro.cache.set({ maxAge: 3600, swr: 60, tags: ['post-8421'] }). O endpoint que recebe o webhook do WordPress chama context.cache.invalidate({ tags: ['post-8421'] }) ou invalidate({ path: '/blog/my-post' }). A invalidação por caminho só funciona com correspondência exata, sem caracteres universais. As routeRules em astro.config.mjs definem valores por omissão para grupos de rotas, por exemplo um maxAge para /blog/[...slug], e uma página pode substituí-los ou sair da cache com Astro.cache.set(false) quando a resposta é personalizada.
A cache propriamente dita vem de um fornecedor. O memoryCache() incluído serve para um único servidor Node. Os adaptadores para Netlify, Vercel e Cloudflare trazem fornecedores CDN que ainda são experimentais e têm de ser ativados à mão. O fornecedor da Cloudflare, a partir de @astrojs/cloudflare 14, define os cabeçalhos Cloudflare-CDN-Cache-Control e Cache-Tag e ativa a cache do Workers. Em desenvolvimento, a API de cache não faz nada, por isso teste-a com um build e uma pré-visualização.
Como misturar partes estáticas e dinâmicas numa página do WordPress
Muitas rotas são estáveis exceto num elemento em tempo real: uma página de produto com o stock, um artigo com o avatar do leitor com sessão iniciada. Renderizar a página inteira a cada pedido por causa desse elemento é a solução cara. As server islands do Astro são a mais barata (server islands no Astro). Acrescente server:defer ao componente que precisa de dados frescos. O resto da página continua pré-renderizado ou em cache, e a ilha é pedida ao seu próprio endpoint depois de a página carregar. Até lá, um elemento filho com slot="fallback" ocupa o seu lugar, e cada ilha carrega de forma independente, por isso uma consulta de stock lenta não atrasa mais nada.
Aplicam-se dois limites. As server islands precisam de um adaptador, e as suas props têm de ser serializáveis: objetos simples, strings, números, arrays, datas e mais alguns tipos, mas não funções. Numa página de produto do WooCommerce, isso significa passar o ID do produto e não um cliente da API.
Isto afina a tabela acima. A rota continua estática ou em ISR, e só a ilha é renderizada a cada pedido.
Como obter conteúdo do WordPress para builds estáticos e ISR
Cada passo de pré-renderização começa com uma lista de slugs, e a REST API do WordPress devolve-a por páginas. per_page aceita valores de 1 a 100, e cada resposta traz os cabeçalhos X-WP-Total e X-WP-TotalPages, pelo que um script de build lê a primeira página e percorre até ao número de páginas indicado no cabeçalho (paginação na REST API). O Next.js passa essa lista a generateStaticParams. Os artigos publicados depois do build são renderizados no primeiro pedido, a menos que dynamicParams seja false, e generateStaticParams não volta a ser chamado durante a revalidação (referência de generateStaticParams).
O parâmetro _fields reduz a resposta ao que o passo precisa, por exemplo ?_fields=slug,modified_gmt para a lista de slugs, e o WordPress nem sequer calcula os campos omitidos. Desde o WordPress 5.3 aceita também chaves meta aninhadas, e desde o 5.4 _embed pode ser limitado a relações com nome, como author,wp:term (parâmetros globais da REST API).
Para detetar alterações, o endpoint de artigos aceita modified_after com uma data ISO 8601 e devolve modified_gmt em cada artigo (referência do endpoint posts). Uma tarefa agendada que pede os artigos alterados desde a última execução é uma boa rede de segurança para webhooks perdidos, e mais barata do que uma janela curta de revalidate em todas as rotas.
Como pré-visualizar rascunhos do WordPress num front-end com ISR
Os editores esperam que a pré-visualização mostre o rascunho, enquanto um front-end em cache serve a versão publicada. O Draft Mode do Next.js resolve isto por visitante (guia do Draft Mode). Um Route Handler verifica um segredo partilhado, chama draftMode().enable(), que define o cookie __prerender_bypass, e redireciona para a página. Os pedidos com esse cookie ignoram a cache de fetch e a cache de respostas do ISR e são enviados com Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate. Todos os outros visitantes continuam a receber a página em cache.
Do lado do WordPress, o endpoint de artigos devolve conteúdo publicado salvo indicação em contrário, porque status tem publish como valor por omissão. Os rascunhos são, por isso, lidos com pedidos autenticados. As senhas de aplicação, incluídas no WordPress desde a versão 5.6 e enviadas como Basic Auth por HTTPS, são o método que o manual documenta (autenticação na REST API). Mantenha essa credencial no servidor, no Route Handler ou numa chamada fetch que só corra no servidor, e nunca no código do cliente.
Guias relacionados sobre WordPress headless
Este artigo faz parte do pilar do serviço headless WordPress. Para a escolha ao nível do framework, consulte a matriz de decisão Next.js ou Astro. Os riscos de uma migração estão reunidos na lista de padrões SEO para headless WordPress, o que é específico de uma loja em headless WordPress para WooCommerce e a parte dos custos no artigo sobre a economia do headless WordPress. Para uma comparação dos frameworks versão a versão, incluindo a forma como cada um carrega os dados do WordPress, consulte Astro 5 vs Next.js 15.
A parte de implementação deste tema está a nosso cargo no âmbito da auditoria Core Web Vitals.







