Alternar tema

Geração de páginas com templates: caminhos técnicos para implementar SEO programático

Easton editorial illustration: SEO-to-publishing automation line

Na semana passada, um amigo veio conversar comigo sobre SEO programático. Ele já havia preparado sua matriz de palavras-chave, com mais de duas mil buscas de cauda longa organizadas em uma planilha do Excel, mas não sabia como avançar.

“Eu entendo a lógica, mas, na hora de colocar a mão na massa, surgem dúvidas por todos os lados. Uso Next.js ou Astro? Qual banco de dados escolho? Como estruturo as URLs? E, se eu gerar cinco mil páginas de uma vez, o servidor vai aguentar?”

Eu me fazia as mesmas perguntas dois anos atrás. Na época, estava criando um diretório de serviços jurídicos e pretendia usar SEO programático para publicar três mil páginas de cidades. O resultado? Levei dois meses para colocar a primeira versão no ar e ainda sinto dor de cabeça ao lembrar dos problemas que encontrei.

Este artigo existe para poupar você dessas armadilhas. Vou apresentar três caminhos técnicos completos: geração estática, renderização dinâmica e uma arquitetura híbrida. Cada um inclui a lógica do código, os cenários adequados e casos reais. Ao terminar a leitura, você já poderá começar — ao menos sem precisar explorar tudo do zero como eu fiz.


Antes de tudo: qual caminho combina com o seu projeto?

Implementar uma solução não significa escolher um framework às cegas. Primeiro, você precisa entender as características dos seus dados.

A geração estática (SSG) serve para você?

Se os dados mudam pouco, talvez uma vez por semana ou até por mês, a geração estática é a opção mais segura.

Um exemplo: certa vez ajudei um amigo a criar um site de guias de viagem. O conteúdo de cada cidade — atrações, transporte e recomendações gastronômicas — era praticamente fixo e só precisava ser atualizado a cada seis meses. Usamos Content Collections do Astro, armazenamos os dados de duas mil cidades em arquivos JSON e geramos as páginas estáticas em lote. A compilação levava cerca de dez minutos, mas, depois da publicação, o TTFB (tempo até o primeiro byte) se mantinha próximo de 80 ms e a taxa de acerto do cache da CDN chegava a 95%.

As vantagens são claras: carregamento rápido, boa base técnica para SEO e pouca pressão sobre o servidor. Mas há limitações: qualquer atualização exige recompilar o site inteiro, e o tempo de build cresce bastante quando o projeto passa de cinco mil páginas.

Quando usar renderização dinâmica (SSR)?

Quando os dados mudam em tempo real, a geração estática deixa de ser suficiente.

A Wise, plataforma de transferências internacionais, é um exemplo típico. As taxas de câmbio em suas páginas de conversão mudam a cada minuto. Com geração estática, o usuário poderia ver uma cotação de dez minutos atrás, uma diferença importante na hora de decidir uma transferência. Por isso, essas páginas usam SSR (renderização no servidor) do Next.js e consultam a API a cada solicitação para obter a cotação mais recente.

O custo da renderização dinâmica é a carga sobre o servidor. A Wise recebe milhões de consultas de conversão por dia, portanto a infraestrutura não é barata. Além disso, o TTFB tende a ser maior, normalmente entre 200 e 500 ms.

A arquitetura híbrida é o meio-termo

Se o projeto reúne dados que mudam pouco e outros que mudam com frequência, uma arquitetura híbrida pode ser a melhor escolha.

As páginas de integrações do Zapier seguem esse modelo. Existem mais de cinco mil páginas que combinam “App A + App B”, como “integração entre Slack e Gmail”. As informações básicas — descrição dos recursos e etapas de configuração — são estáticas, mas o estado real da integração de cada usuário — se está conectada e quando ocorreu a última sincronização — é dinâmico.

O Zapier usa ISR (regeneração estática incremental) do Next.js. O primeiro carregamento entrega uma página estática, enquanto um mecanismo em segundo plano realiza atualizações periódicas. Assim, o conteúdo chega rápido sem ficar desatualizado.

Minha recomendação: responda a estas três perguntas antes de escolher o caminho técnico.

  1. Com que frequência os dados mudam? Diariamente, por hora ou em tempo real?
  2. Qual é a escala do projeto? Menos de cinco mil páginas, entre cinco e vinte mil ou mais de vinte mil?
  3. Qual é a exigência de desempenho para SEO? O TTFB precisa ficar abaixo de 100 ms ou 300 ms ainda é aceitável?

Depois de esclarecer esses três pontos, a escolha fica muito mais simples.

<100 ms
TTFB com geração estática
Cache da CDN
200–500 ms
TTFB com renderização dinâmica
Geração no servidor
Equilíbrio
Solução com ISR
Desempenho e atualização
Source: Comparação de indicadores técnicos

Geração estática: implementação prática com Astro

Se você optar pela geração estática, recomendo o Astro. O motivo é simples: ele foi projetado para sites estáticos, e o recurso Content Collections funciona especialmente bem com SEO programático.

Estrutura dos dados

Primeiro, defina a estrutura dos dados. Imagine que você vai criar um diretório de serviços jurídicos, com uma página para cada cidade. Os dados podem ser organizados assim:

// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const lawyersCollection = defineCollection({
  type: 'content',
  schema: z.object({
    city: z.string(),
    citySlug: z.string(),
    province: z.string(),
    lawyerCount: z.number(),
    topFirms: z.array(z.string()),
    avgPrice: z.string(),
    specialties: z.array(z.string()),
  }),
});

export const collections = {
  'lawyers': lawyersCollection,
};

Depois, crie os arquivos de dados no diretório src/content/lawyers/, um JSON para cada cidade:

// src/content/lawyers/beijing.json
{
  "city": "Pequim",
  "citySlug": "beijing",
  "province": "Pequim",
  "lawyerCount": 12500,
  "topFirms": ["King & Wood Mallesons", "Zhong Lun", "Dacheng"],
  "avgPrice": "2.000–5.000 yuans por hora",
  "specialties": ["Penal", "Cível", "Empresarial", "Propriedade intelectual"]
}

Duas mil cidades significam dois mil arquivos JSON. Parece trabalhoso, mas você pode automatizar tudo com um script. Normalmente uso Python ou Node.js para exportar os dados do banco e gravar os arquivos JSON em lote.

Template de rota dinâmica

Com os dados prontos, é hora de criar o template. As rotas dinâmicas do Astro são bastante flexíveis:

// src/pages/[citySlug].astro
---
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const lawyers = await getCollection('lawyers');
  return lawyers.map(lawyer => ({
    params: { citySlug: lawyer.data.citySlug },
    props: { lawyer },
  }));
}

const { lawyer } = Astro.props;
---

<!DOCTYPE html>
<html>
<head>
  <title>Guia de serviços jurídicos em {lawyer.data.city} | Escritórios recomendados e valores</title>
  <meta name="description" content={`Guia completo de serviços jurídicos em ${lawyer.data.city}, com áreas como ${lawyer.data.specialties.join(', ')}, escritórios de destaque como ${lawyer.data.topFirms.join(', ')} e valores médios de ${lawyer.data.avgPrice}`} />
</head>
<body>
  <h1>Guia de serviços jurídicos em {lawyer.data.city}</h1>

  <section>
    <h2>Dados principais</h2>
    <p>Número de advogados: {lawyer.data.lawyerCount}</p>
    <p>Principais áreas: {lawyer.data.specialties.join(', ')}</p>
    <p>Valor médio: {lawyer.data.avgPrice}</p>
  </section>

  <section>
    <h2>Escritórios recomendados</h2>
    <ul>
      {lawyer.data.topFirms.map(firm => <li>{firm}</li>)}
    </ul>
  </section>

  <!-- Links internos: cidades relacionadas -->
  <section>
    <h2>Serviços jurídicos em cidades próximas</h2>
    <!-- Aqui você pode recomendar cidades por província ou localização -->
  </section>
</body>
</html>

A função getStaticPaths gera automaticamente a página estática de cada cidade. Duas mil cidades produzem dois mil arquivos HTML.

Build e implantação

O comando de build do Astro é simples:

npm run build

Depois da compilação, o diretório dist/ contém todos os arquivos HTML estáticos. Basta implantá-lo no Cloudflare Pages ou na Vercel para que a CDN faça o cache automaticamente.

No site de guias de viagem que mencionei, a compilação de duas mil páginas levava cerca de dez minutos. O Astro realmente é rápido nesse cenário e costuma ser mais eficiente do que a geração estática do Next.js.

Dica de otimização: se o projeto passar de cinco mil páginas, divida a compilação em lotes. O Astro oferece suporte à compilação incremental, o que permite gerar apenas as páginas correspondentes aos novos dados sem refazer o site inteiro.


Renderização dinâmica: implementação prática com SSR do Next.js

Se os seus dados mudam em tempo real, a geração estática não atende ao requisito. Nesse caso, use SSR do Next.js.

Configuração básica

O núcleo do SSR no Next.js é getServerSideProps:

// pages/currency/[pair].tsx
import { GetServerSideProps } from 'next';

export const getServerSideProps: GetServerSideProps = async (context) => {
  const { pair } = context.params;
  const [from, to] = pair.split('-to-');

  // Busca a taxa de câmbio em tempo real
  const exchangeRate = await fetchExchangeRate(from, to);

  return {
    props: {
      from,
      to,
      rate: exchangeRate.rate,
      lastUpdate: exchangeRate.timestamp,
    },
  };
};

export default function CurrencyPage({ from, to, rate, lastUpdate }) {
  return (
    <div>
      <h1>Conversão de {from} para {to}</h1>
      <p>Taxa atual: {rate}</p>
      <p>Atualizado em: {new Date(lastUpdate).toLocaleString()}</p>

      {/* Calculadora de conversão */}
      <input type="number" placeholder="Digite o valor" />
      <button>Converter</button>

      {/* Gráfico histórico */}
      <div>Variação da taxa nos últimos 30 dias</div>
    </div>
  );
}

Sempre que alguém acessa /currency/usd-to-eur, o servidor consulta a API de câmbio para obter os dados mais recentes. Assim, o conteúdo da página permanece atualizado.

Estratégia de cache: não sobrecarregue o servidor

O problema da renderização dinâmica é a pressão sobre o servidor. A Wise recebe milhões de consultas por dia; buscar dados em tempo real a cada solicitação elevaria demais os custos.

A solução é usar cache, mas a estratégia precisa ser bem definida.

stale-while-revalidate funciona muito bem nesse caso. Quando chega uma solicitação, o servidor primeiro devolve o dado em cache, mesmo que esteja desatualizado há alguns minutos, e faz a atualização silenciosamente em segundo plano. Na próxima visita, o conteúdo novo já estará disponível.

O Next.js permite implementar essa estratégia:

export const getServerSideProps: GetServerSideProps = async (context) => {
  const { pair } = context.params;

  // Verifica o cache
  const cached = await checkCache(pair);

  if (cached && !isExpired(cached)) {
    return { props: cached.data };
  }

  // Se o cache expirou, atualiza em segundo plano
  fetchExchangeRate(pair).then(data => updateCache(pair, data));

  // Por enquanto, devolve os dados anteriores
  return { props: cached?.data || await fetchExchangeRate(pair) };
};

Com isso, o usuário sempre vê o conteúdo rapidamente e o servidor não precisa consultar uma API externa em todas as solicitações.

Inserção dinâmica de dados estruturados

Em páginas renderizadas dinamicamente, os dados estruturados também precisam ser gerados de forma dinâmica:

// Gera o JSON-LD dentro do componente da página
const jsonLd = {
  "@context": "https://schema.org",
  "@type": "FinancialService",
  "name": `${from} to ${to} Currency Conversion`,
  "offers": {
    "@type": "Offer",
    "price": rate,
    "priceCurrency": to,
  },
};

// Insere o conteúdo no HTML
<script type="application/ld+json">
  {JSON.stringify(jsonLd)}
</script>

Assim, os dados estruturados de cada página permanecem corretos em tempo real, e os resultados do Google também podem exibir a taxa mais recente.


Arquitetura híbrida: implementação prática com ISR do Next.js

Se o seu cenário combina partes estáticas e dinâmicas, o ISR (regeneração estática incremental) é uma ótima escolha.

A lógica central do ISR

O princípio do ISR é este: a página é estática quando gerada pela primeira vez, mas recebe um “prazo de validade”. Depois que esse prazo termina, a próxima visita aciona uma nova geração em segundo plano.

// pages/integrations/[app1]-and-[app2].tsx
export async function getStaticPaths() {
  const integrations = await fetchAllIntegrations();
  return integrations.map(int => ({
    params: { app1: int.app1, app2: int.app2 },
  }));
}

export async function getStaticProps({ params }) {
  const integration = await fetchIntegration(params.app1, params.app2);

  return {
    props: integration,
    revalidate: 3600, // Expira após uma hora
  };
}

revalidate: 3600 significa que todas as visitas feitas durante a primeira hora recebem o conteúdo estático. Depois disso, o primeiro visitante ainda vê a versão anterior, mas dispara uma nova geração em segundo plano. O visitante seguinte já recebe a página atualizada.

Atualização sob demanda: on-demand revalidation

Em alguns cenários, não dá para esperar a expiração natural. Imagine que uma integração do Zapier pare de funcionar. Depois de receber o relato de um usuário, você quer atualizar o estado da página imediatamente, e não uma hora depois.

O Next.js oferece revalidação sob demanda:

// Rota de API que dispara a atualização
// pages/api/revalidate.ts
export default async function handler(req, res) {
  const { app1, app2 } = req.query;

  try {
    await res.revalidate(`/integrations/${app1}-and-${app2}`);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

Você pode configurar um script de monitoramento que verifique periodicamente o estado das integrações. Ao detectar um problema, ele chama essa API para atualizar a página.

Os limites do ISR

O ISR não resolve tudo. Se os dados realmente precisam estar atualizados em toda visita, como cotações de ações ou promoções-relâmpago, ainda será necessário usar SSR.

O ISR funciona melhor quando os dados mudam com pouca frequência — a cada hora ou uma vez por dia —, mas precisam aparecer rapidamente após uma atualização. As páginas de integração do Zapier são um exemplo perfeito: as informações podem ficar iguais por meses, mas, quando mudam, os usuários querem ver a novidade quanto antes.


Estrutura de URLs: a base do SEO

Depois de definir a tecnologia, é hora de planejar a estrutura das URLs. É comum subestimar essa etapa, mas ela afeta diretamente os resultados de SEO.

Três princípios para URLs amigáveis ao SEO

Primeiro, inclua a palavra-chave principal. A URL é um dos fatores observados pelo Google, e inserir o termo de forma natural pode ajudar.

Para uma página sobre “advogado de divórcio em Pequim”, por exemplo, a URL poderia ser /beijing/divorce-lawyer. Os termos “Pequim” e “advogado de divórcio” aparecem naturalmente no caminho.

Segundo, não ultrapasse três níveis. Caminhos profundos demais prejudicam usuários e rastreadores.

Uma URL com seis níveis, como /service/legal/lawyer/divorce/beijing, é difícil de entender à primeira vista. O Google também pode interpretá-la como sinal de uma página de baixa qualidade.

Terceiro, separe palavras com hífens em vez de usar parâmetros.

Uma URL parametrizada como /lawyer?type=divorce&city=beijing tende a funcionar pior para SEO do que /beijing/divorce-lawyer. Parâmetros também podem levar o rastreador a interpretar a página como altamente dinâmica, reduzindo a eficiência da indexação.

Três padrões comuns de URL

Encontrei três padrões predominantes, cada um adequado a um tipo de projeto.

Padrão 1: termo principal primeiro

/lawyer/beijing/divorce

É indicado para sites orientados pela marca. O termo principal, lawyer, aparece primeiro e reforça o posicionamento do site.

Padrão 2: localização primeiro

/beijing/divorce-lawyer

É indicado para sites de serviços locais. Quando a pessoa pesquisa “advogado de divórcio em Pequim”, a URL corresponde diretamente à intenção e pode ter uma vantagem de relevância.

Padrão 3: estrutura plana

/beijing-divorce-lawyer

É adequado para sites com um volume muito grande de páginas. Há apenas um nível de caminho, o que simplifica a compilação e a manutenção.

Minha recomendação é observar como o seu público pesquisa. Se a maioria das consultas segue o padrão “cidade + serviço”, use o segundo modelo. Se os termos de busca forem mais variados, o terceiro oferece maior flexibilidade.

Com as URLs definidas, o próximo passo são os links internos. Uma das vantagens do SEO programático é poder criar automaticamente uma rede de páginas relacionadas.

Imagine um diretório jurídico com três mil páginas de cidades. Cada uma delas deve apontar para localidades e serviços relacionados. Como fazer isso?

Por hierarquia geográfica: a página de Pequim pode apontar para “advogados na província de Hebei” e “advogados em Tianjin”, que são regiões próximas.

Por tipo de serviço: uma página de advogados de divórcio em Pequim pode apontar para “advogados criminalistas em Pequim” e “advogados cíveis em Pequim”, mantendo a cidade e variando a especialidade.

A implementação é simples:

---
// Dentro do template da página
const { lawyer } = Astro.props;
const nearbyCities = await getNearbyCities(lawyer.data.province);
const relatedSpecialties = lawyer.data.specialties;
---

<section>
  <h2>Advogados em cidades próximas</h2>
  {nearbyCities.map(city => (
    <a href={`/${city.slug}/${lawyer.data.specialties[0]}-lawyer`}>
      Advogados de {lawyer.data.specialties[0]} em {city.name}
    </a>
  ))}
</section>

<section>
  <h2>Outros serviços jurídicos em {lawyer.data.city}</h2>
  {relatedSpecialties.map(spec => (
    <a href={`/${lawyer.data.citySlug}/${spec}-lawyer`}>
      Advogados de {spec} em {lawyer.data.city}
    </a>
  ))}
</section>

Assim, cada página ganha dezenas de links internos e o site forma uma rede. Isso aumenta a eficiência do rastreamento e também ajuda o usuário a encontrar rapidamente conteúdos relacionados.


Escolha do banco de dados: não perca tempo demais aqui

Você já definiu a estrutura dos dados; agora precisa decidir onde armazená-los. Muita gente fica presa nessa escolha, embora ela não seja tão complicada.

Quatro opções para cenários diferentes

PostgreSQL: dados estruturados e consultas complexas.

Quando os campos são estáveis e você precisa fazer consultas elaboradas — por exemplo, “encontre todos os advogados de divórcio em Pequim que cobram menos de 3.000” —, PostgreSQL é a escolha mais segura. Você conta com garantias ACID, suporte a transações e busca de texto completo.

MongoDB: estrutura flexível e iterações rápidas.

Se a estrutura dos dados ainda está mudando, MongoDB oferece mais flexibilidade. Não é necessário definir o schema com antecedência e novos campos podem ser adicionados a qualquer momento. Eu usava muito MongoDB nos primeiros projetos justamente porque a estrutura mudava com frequência.

Airtable/Google Sheets: pequena escala e colaboração.

Se você tem apenas algumas dezenas ou centenas de registros e várias pessoas precisam colaborar, Airtable ou Google Sheets podem funcionar muito bem. Eles oferecem edição visual, colaboração em tempo real e são acessíveis a pessoas sem experiência técnica. Um amigo mantém um pequeno projeto no Airtable com duzentos registros e um custo operacional muito baixo.

Arquivos CSV/JSON: cenários totalmente estáticos.

Se os dados não mudam e o projeto tem menos de mil páginas, arquivos CSV ou JSON são suficientes. Você não precisa manter um banco: basta ler os arquivos durante a compilação. Content Collections do Astro foi pensado justamente para esse tipo de fluxo.

Minha recomendação: comece pelo volume de dados

Menos de mil registros: arquivos CSV/JSON ou Airtable.
Entre mil e dez mil registros: PostgreSQL ou MongoDB.
Mais de dez mil registros: PostgreSQL com cache Redis.

Não fique paralisado nessa decisão. Escolha algo que resolva o problema atual. Se a estrutura mudar mais adiante, migrar os dados não será um projeto gigantesco.


O ponto decisivo dos templates: diferenciar o conteúdo

Com a arquitetura pronta, o verdadeiro desafio passa a ser desenvolver os templates. Muitos projetos de SEO programático fracassam porque suas páginas são parecidas demais.

Armadilha: um template que só troca palavras-chave está condenado

Já vi um caso que terminou mal. Um site criou vinte mil páginas de “cidade + hotel”, mudando apenas o nome da cidade e mantendo todo o resto idêntico. “Hotéis em Pequim”, “hotéis em Xangai”, “hotéis em Guangzhou” — o corpo do texto era sempre o mesmo.

O resultado foi uma penalização do algoritmo do Google. O tráfego caiu 70%, e a recuperação levou oito meses.

A causa do problema: as páginas não ofereciam valor exclusivo. Quem pesquisava “hotéis em Pequim” encontrava praticamente o mesmo conteúdo de “hotéis em Xangai”, um sinal evidente de conteúdo em massa sem utilidade.

Três formas de criar diferenciação

Método 1: inserção de dados dinâmicos

Cada página precisa trazer dados próprios. Em um diretório jurídico, o número de advogados, a lista de escritórios e os valores médios variam em cada cidade. Como esses dados vêm diretamente do banco, as páginas já nascem diferentes.

Método 2: integração de UGC

Conteúdo gerado por usuários é uma excelente fonte de diferenciação. Cada página de hotel no TripAdvisor contém avaliações específicas escritas por pessoas reais, e não por um template.

Se você possui UGC, integre-o às páginas. Em um diretório jurídico, por exemplo, pode criar um módulo de avaliações:

<section>
  <h2>Avaliações dos usuários</h2>
  {lawyer.data.reviews.map(review => (
    <div>
      <p>{review.content}</p>
      <span>Nota: {review.rating}/5</span>
    </div>
  ))}
</section>

Método 3: expansão assistida por IA

Quando não há dados dinâmicos ou UGC, a IA pode ajudar a criar conteúdo diferenciado. Mas há uma condição: todo conteúdo gerado precisa passar por revisão humana e deve ser usado apenas em trechos descritivos, nunca como conteúdo principal.

Fiz um experimento em um diretório jurídico. Os dados centrais — quantidade de advogados e lista de escritórios — vinham do banco, enquanto uma apresentação sobre “características dos serviços jurídicos” de cada cidade era produzida com ajuda de IA. Em Pequim, por exemplo, o texto destacava a alta proporção de disputas empresariais e a grande demanda por propriedade intelectual.

O ponto principal: a IA serve para ampliar, não para substituir. Cada página ainda precisa conter dados reais e exclusivos; a IA é apenas um complemento.

Automação de dados estruturados

Cada página deve incluir dados estruturados em JSON-LD, algo que pode ser completamente automatizado:

const jsonLd = {
  "@context": "https://schema.org",
  "@type": "LegalService",
  "name": `Serviços jurídicos em ${lawyer.data.city}`,
  "areaServed": {
    "@type": "City",
    "name": lawyer.data.city,
  },
  "provider": lawyer.data.topFirms.map(firm => ({
    "@type": "Organization",
    "name": firm,
  })),
};

Os dados estruturados de cada página são gerados a partir de dados reais. Assim, os resultados do Google podem exibir os escritórios e a cidade, o que tende a melhorar a taxa de cliques.


Otimização de desempenho: não deixe o usuário esperando

Como projetos de SEO programático costumam ter muitas páginas, o desempenho não pode ser ignorado.

Três métricas principais

TTFB (tempo até o primeiro byte): mede a velocidade de resposta do servidor.

Na geração estática, normalmente fica abaixo de 100 ms; na renderização dinâmica, entre 200 e 500 ms. Se o TTFB passar de 500 ms, o usuário pode abandonar a página antes mesmo de ver o conteúdo.

LCP (Largest Contentful Paint): mede o tempo até o principal conteúdo aparecer.

A meta é ficar abaixo de 2,5 segundos. Se a página tiver muitas imagens ou componentes complexos, o LCP pode aumentar bastante.

FID (First Input Delay): mede o tempo de resposta às interações.

A meta é ficar abaixo de 100 ms. Quando há JavaScript demais, o usuário pode clicar em um botão sem obter resposta imediata.

Configuração da CDN: acelere páginas estáticas

Para páginas geradas estaticamente, o cache da CDN é essencial. Cloudflare Pages e Vercel já oferecem CDN, e a configuração é simples.

// astro.config.mjs
export default defineConfig({
  output: 'static',
  build: {
    assets: 'assets/',
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          assetFileNames: 'assets/[hash][extname]',
        },
      },
    },
  },
});

Com essa configuração, cada arquivo estático recebe um hash exclusivo, aumentando a eficiência do cache da CDN.

Otimização de imagens: não deixe os arquivos pesados atrasarem a página

Se a imagem hero de cada página for usada diretamente em JPG, ela pode ter vários megabytes e demorar muito para carregar.

O Astro inclui otimização de imagens:

---
import { Image } from 'astro:assets';
import heroImage from '../images/lawyer-hero.jpg';
---

<Image src={heroImage} alt="Serviços jurídicos" width={1200} height={675} />

O Astro converte automaticamente a imagem para WebP e a comprime para um tamanho adequado. Um arquivo de 2 MB pode cair para apenas 200 KB.

Gestão do orçamento de rastreamento: indispensável para muitos milhares de páginas

Quando o projeto passa de cinco mil páginas, talvez o rastreador do Google não consiga visitar todas. Esse problema é conhecido como desperdício do orçamento de rastreamento.

Soluções:

Primeiro, divida o sitemap.xml. Em vez de colocar cinco mil páginas em um único sitemap, crie vários arquivos:

// sitemap-index.xml
<sitemapindex>
  <sitemap><loc>https://example.com/sitemap-1.xml</loc></sitemap>
  <sitemap><loc>https://example.com/sitemap-2.xml</loc></sitemap>
  <sitemap><loc>https://example.com/sitemap-3.xml</loc></sitemap>
</sitemapindex>

Cada sitemap pode ter no máximo 500 páginas nesse modelo, facilitando o rastreamento.

Segundo, priorize os links internos. Páginas importantes, como cidades com maior volume de busca, devem receber mais entradas por meio de destaques na página inicial e no menu. Páginas menos importantes podem receber menos links, levando o rastreador a priorizar naturalmente o que mais importa.


Estudos de caso reais: como outros sites fazem

Depois da teoria, vamos observar casos reais. A arquitetura desses sites oferece boas referências.

TripAdvisor: um exemplo de arquitetura híbrida

O TripAdvisor possui milhões de páginas de hotéis. Como isso é possível?

Arquitetura: combinação de geração estática e atualizações dinâmicas.

As informações básicas dos hotéis, como nome, endereço e comodidades, são geradas estaticamente. As avaliações e notas dos usuários são carregadas dinamicamente. Cada página usa duas fontes: dados estáticos exportados do banco e dados dinâmicos vindos da API de avaliações.

Estrutura da URL: /hotel/[city]/[hotel-name]

Por exemplo, /hotel/beijing/grand-hyatt. O caminho tem três níveis e é amigável ao SEO.

Tecnologias principais:

  • Atualização das avaliações em tempo real por meio de UGC
  • Carregamento dinâmico da comparação de preços via API
  • Automação de dados estruturados com o schema Review

O ponto decisivo do TripAdvisor é o UGC. Cada hotel reúne centenas de avaliações reais, o que diferencia naturalmente o conteúdo. Sem UGC, páginas puramente geradas por template dificilmente alcançariam o mesmo desempenho.

Zapier: um caso clássico de ISR

O Zapier possui mais de cinco mil páginas de integrações no formato “App A + App B”, como “integração entre Slack e Gmail” e “integração entre Notion e Google Calendar”.

Arquitetura: ISR do Next.js.

As informações básicas da integração, como apresentação dos recursos e etapas de configuração, são estáticas. O estado específico do usuário, como conexão ativa e horário da última sincronização, é dinâmico. O ISR mantém as páginas rápidas sem deixá-las desatualizadas.

Estrutura da URL: /integrations/[app1]/[app2]

Por exemplo, /integrations/slack/gmail. São dois níveis, simples e claros.

Tecnologias principais:

  • on-demand revalidation: atualiza a página assim que uma integração apresenta problema
  • Cobertura com testes automatizados: Playwright testa cada página de integração
  • Rede de links internos: páginas centrais de aplicativos e recomendações de integrações relacionadas

A implementação de ISR do Zapier merece atenção. A equipe de engenharia publicou um artigo sobre como gerencia cinco mil páginas com ISR; vale a leitura.

Wise: renderização dinâmica com estratégia de cache

Nas páginas de conversão de moedas da Wise, as taxas mudam a cada minuto. Uma solução totalmente estática não funcionaria.

Arquitetura: SSR do Next.js com cache stale-while-revalidate.

Quando alguém acessa /currency/usd-to-eur, o servidor verifica primeiro o cache. Se ele ainda for válido — por exemplo, se tiver menos de cinco minutos —, os dados armazenados são devolvidos imediatamente. A taxa é atualizada silenciosamente em segundo plano, e o próximo visitante já recebe os novos dados.

Estrutura da URL: /currency/[from]-to-[to]

Por exemplo, /currency/usd-to-eur. As palavras-chave aparecem naturalmente no caminho.

Tecnologias principais:

  • Estratégia de cache stale-while-revalidate
  • CDN edge caching: cache distribuído em pontos de presença globais
  • Inserção dinâmica de dados estruturados: JSON-LD com a taxa em tempo real

A estratégia de cache da Wise é muito bem equilibrada: preserva a atualização dos dados e mantém os custos de servidor sob controle. Se você trabalha com dados em tempo real, esse modelo é uma boa referência.


Armadilhas: o que aprendi errando

Já falamos de casos bem-sucedidos; agora quero abordar os erros. Eu mesmo caí em todos eles e espero que você consiga evitá-los.

Armadilha 1: estrutura de URL confusa

Na primeira versão do meu diretório jurídico, as URLs tinham este formato: /service?id=lawyer&city=beijing&type=divorce.

Parecia flexível, mas trouxe muitos problemas. O rastreador do Google não lida tão bem com URLs parametrizadas, e a indexação se torna menos eficiente. Além disso, quem vê esse endereço não consegue entender facilmente o conteúdo da página.

Lição: defina a estrutura das URLs antes de começar a desenvolver. Alterá-la depois da publicação custa caro, porque exige atualizar links internos, backlinks e sitemaps.

Armadilha 2: conteúdo repetido nos templates

Vi um site criar vinte mil páginas e trocar apenas o nome da cidade. O texto era idêntico; só “Pequim” virava “Xangai”.

O Google aplicou uma penalização, e o tráfego caiu 70%.

Lição: cada página precisa ter dados exclusivos. Se você não dispõe desses dados, não gere a página. É melhor publicar mil páginas de qualidade do que dez mil páginas sem valor.

Armadilha 3: gargalo de desempenho

Renderizar dinamicamente cinco mil páginas exige muito do servidor. Em um projeto antigo, usei PHP para gerar tudo dinamicamente e o servidor caía com frequência.

Lição: acima de cinco mil páginas, use geração estática ou ISR. Uma arquitetura totalmente dinâmica pode não suportar o volume.

Armadilha 4: ausência de monitoramento

Sem monitoramento depois da publicação, os problemas aparecem tarde demais. Em um dos meus projetos, só percebi três meses depois que metade das páginas não havia sido indexada pelo Google por causa de um erro no sitemap.

Lição: monitore desde a primeira semana. Use o Google Search Console para verificar a indexação, o Screaming Frog para encontrar problemas técnicos e o Ahrefs para acompanhar mudanças de posição.


Próximos passos: pare de pensar e comece pequeno

Depois de tanta informação, talvez você ainda esteja um pouco perdido. Minha recomendação é não tentar resolver tudo de uma vez: faça um pequeno experimento.

Etapa 1: escolha um cenário pequeno

Não comece com cinco mil páginas. Escolha um caso controlável, como cinquenta páginas de cidades.

Etapa 2: defina a stack

Se os dados mudam pouco, use Astro. Se mudam em tempo real, use SSR do Next.js.

Etapa 3: planeje os dados e as URLs

Reserve meio dia para definir os campos e os caminhos das URLs. Essa etapa é importante; não tente economizar tempo nela.

Etapa 4: desenvolva e teste o template

Crie primeiro três páginas e revise manualmente o grau de diferenciação do conteúdo. Depois de confirmar a qualidade, gere o restante em lote.

Etapa 5: publique um lote pequeno

Coloque cinquenta páginas no ar e observe os resultados durante uma semana. Avalie a taxa de indexação, a rejeição e o tempo de permanência. Se os indicadores estiverem saudáveis, aumente para quinhentas páginas.

Etapa 6: continue melhorando

Ajuste templates, links internos e URLs de acordo com os dados observados. SEO programático não é uma tarefa pontual, mas um processo contínuo de iteração.


FAQ

Como escolher entre geração estática, renderização dinâmica e uma arquitetura híbrida?
Considere a frequência de atualização dos dados e o número de páginas. Para dados atualizados semanal ou mensalmente e menos de cinco mil páginas, use geração estática com Astro. Para dados em tempo real, use renderização dinâmica com SSR do Next.js. Entre esses dois cenários, adote uma arquitetura híbrida com ISR.
Existe um limite de páginas para SEO programático?
Não há um limite rígido, mas é preciso considerar o orçamento de rastreamento. Em geral, o Google consegue rastrear um site com até cinco mil páginas. Entre cinco e vinte mil páginas, é necessário dividir o sitemap e otimizar os links internos. Acima de vinte mil páginas, é indispensável aplicar critérios rigorosos de qualidade e gerar apenas páginas com demanda de busca.
O Google penaliza páginas geradas com templates?
Não, desde que cada página ofereça valor real. Há três pontos essenciais: dados exclusivos em cada página, e não apenas a troca de palavras-chave; participação dos usuários por meio de UGC ou interações; e uma etapa de revisão humana. Páginas que apenas substituem palavras-chave no mesmo template é que podem ser penalizadas.
Devo escolher PostgreSQL ou MongoDB?
Escolha PostgreSQL quando a estrutura dos dados for estável: suas garantias ACID e consultas complexas são mais robustas. Se a estrutura ainda estiver mudando, o MongoDB oferece um schema mais flexível. Para projetos pequenos, com algumas centenas de registros, Airtable ou arquivos JSON são suficientes.
Quantos níveis uma URL deve ter no máximo?
O recomendado é não passar de três níveis. Um caminho como `/city/service/type` já deve ser o limite. Caminhos profundos demais prejudicam tanto os usuários quanto os rastreadores. URLs planas, com um único nível, funcionam bem em sites com um volume muito grande de páginas.
Como automatizar os links internos?
Há duas estratégias: usar a hierarquia geográfica, ligando páginas de cidades a localidades próximas, ou usar clusters temáticos, criando links entre páginas de serviços semelhantes. No template, basta encontrar páginas relacionadas pelos campos `tags` ou `location` e gerar automaticamente a lista de links.
Astro ou Next.js: qual é melhor para SEO programático?
Para cenários totalmente estáticos, escolha Astro: o recurso Content Collections foi pensado para trabalhar com páginas em escala e a compilação é rápida. Se você precisa de renderização dinâmica, escolha Next.js, que oferece suporte sólido a SSR e ISR. Ambos funcionam para SEO programático; o importante é alinhar a tecnologia à frequência de atualização dos dados.

24 min de leitura · Publicado em: 4 abr 2026 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog