O site está no ar, bonito, rápido no seu navegador, e mesmo assim as páginas internas não aparecem no Google. Antes de culpar o conteúdo ou a concorrência, vale checar uma coisa muito mais simples: o que o crawler recebe quando pede a sua página.
O que o Googlebot recebe quando o HTML vem vazio
Um site feito como SPA, ou single page application, em React, Vue ou Angular, normalmente entrega um HTML assim: uma tag <head>, uma <div id="root"></div> vazia e uma tag de script. Todo o conteúdo, os títulos, os textos, os links de navegação, é criado no navegador depois que o JavaScript baixa e executa.
Para uma pessoa com Chrome moderno isso funciona bem. Para um crawler, o primeiro contato é um documento sem texto. O Googlebot não desiste imediatamente, mas o processo passa a depender de uma etapa extra que você não controla.
E não é só o Google. Pré-visualizações de link no WhatsApp, no LinkedIn e em outras plataformas normalmente leem o HTML bruto e não executam JavaScript. Se as suas meta tags são inseridas por JavaScript, o link compartilhado sai sem título e sem descrição.
Como o Google renderiza JavaScript e por que isso atrasa a indexação
O Google processa páginas com JavaScript em duas etapas. Primeiro rastreia e lê o HTML que veio do servidor. Se o conteúdo depende de JavaScript, a página entra em uma fila de renderização, onde um Chromium controlado pelo Google abre a página, executa os scripts e só então o resultado volta para a indexação.
Essa segunda etapa existe e funciona, mas ela introduz três riscos concretos:
- Atraso. A renderização acontece quando há recursos disponíveis, não imediatamente. Uma página nova ou atualizada pode demorar mais para entrar no índice.
- Falha silenciosa. Se um script quebra, se uma requisição de API expira, se um recurso é bloqueado no
robots.txt, o conteúdo simplesmente não aparece na renderização. E ninguém te avisa. - Dependência de terceiros. Se o seu conteúdo vem de uma API externa lenta, a renderização pode terminar antes do conteúdo chegar.
Nada disso acontece quando o HTML já chega pronto.
Como testar o que o crawler realmente vê
Existem dois testes, e os dois levam menos de dois minutos.
Teste 1: o HTML bruto no terminal. Peça a página como um crawler pediria, sem executar nada:
curl -s https://seusite.com/pagina | grep "<h2"
Se voltar vazio, o seu conteúdo não está no HTML. Troque o grep por um trecho de texto que você sabe que existe na página para confirmar. Um teste ainda mais direto é medir o tamanho do documento:
curl -s https://seusite.com/pagina | wc -c
Um HTML de 800 bytes para uma página com dois mil palavras é o sintoma clássico.
Teste 2: Teste de URL do Search Console. Cole a URL na barra de inspeção, clique em testar URL ativa e depois abra a visualização do HTML rastreado. Ali você vê exatamente o que o Google conseguiu montar, incluindo a captura da página renderizada e a lista de recursos que ele não conseguiu carregar. Se você ainda não configurou o Search Console, comece pelo checklist de SEO técnico.
CSR, SSR e SSG: a diferença em uma frase cada
- CSR, renderização no cliente. O servidor manda um HTML vazio e o navegador monta a página. Simples de hospedar, ruim para crawlers.
- SSR, renderização no servidor. A cada requisição, o servidor executa o código e devolve o HTML pronto. Ótimo para conteúdo que muda a cada acesso, mas exige um servidor Node rodando o tempo todo.
- SSG, geração estática. O HTML de cada página é gerado uma vez, no momento do build, e servido como arquivo. É o mais rápido, o mais barato e o mais seguro para páginas cujo conteúdo não muda a cada segundo.
Para um site institucional, uma landing page ou um blog, SSG é quase sempre a escolha certa. O conteúdo muda algumas vezes por mês, não a cada requisição.
Existe também o caminho híbrido, que é o que a maioria dos sites bem construídos usa hoje: HTML estático para a primeira visita e JavaScript assumindo a navegação depois que a página carrega. O visitante recebe conteúdo imediato, o crawler recebe conteúdo imediato, e a navegação interna continua sem recarregar a página inteira. É disso que trata a próxima seção.
Pré-renderizando um SPA com Playwright
Nem sempre dá para reescrever o projeto em um framework com SSG embutido. E nem precisa. No nosso site, o albseven.com, mantivemos a aplicação em React com Vite e acrescentamos uma etapa de pré-renderização ao processo de build. O script fica em scripts/prerender.mjs e faz o seguinte:
- Depois do build normal, sobe a pasta
dist/em um servidor local comvite preview. - Abre um Chromium via Playwright.
- Visita cada rota da lista de páginas, uma a uma.
- Espera o React terminar de montar a árvore e o conteúdo aparecer de fato.
- Captura o HTML final da página e salva em
dist/<rota>.html. - Fecha o navegador e derruba o servidor de preview.
O resultado é uma pasta de deploy que continua tendo o mesmo SPA para a navegação interna, mas que agora também tem um arquivo HTML completo por rota. Quem chega pela primeira vez recebe HTML pronto. Depois disso, o React assume e a navegação segue instantânea.
Dois detalhes que fazem diferença nesse script. O primeiro é a espera: esperar só o evento de carregamento não basta, porque o React ainda não montou nada. É preciso esperar por um seletor que só existe depois da renderização. O segundo é a lista de rotas, que precisa ser gerada a partir da mesma fonte que alimenta o roteador, senão uma página nova entra no site e fica de fora da pré-renderização sem ninguém perceber.
Um terceiro cuidado é o que acontece com dados vindos de API. Se a página busca conteúdo de um servidor no momento em que carrega, a pré-renderização captura o estado daquele instante e congela no arquivo. Para conteúdo que muda com frequência isso pede uma decisão consciente: ou o build roda a cada publicação, ou a página assume que o conteúdo será atualizado no navegador depois, mantendo o HTML como versão inicial válida para o crawler.
O rewrite no .htaccess que preserva a URL visível
Gerar sobre.html não adianta se a URL pública tiver que virar /sobre.html. A URL limpa é a que já está indexada, é a que as pessoas compartilham e é a que você quer manter. A solução é um rewrite interno no servidor: a URL na barra continua /sobre, mas o Apache entrega o arquivo .html correspondente.
A ideia central, no .htaccess, é: se existe um arquivo com o mesmo nome mais a extensão .html, sirva esse arquivo. Só se não existir é que a requisição cai no index.html do SPA.
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)$ $1.html [L]
Repare no [L] e na ausência de [R]. Não há redirecionamento. O navegador nunca fica sabendo que existe um arquivo .html por trás. É reescrita interna, não redirect, e essa distinção é o que preserva a URL e evita um salto extra em cada acesso.
Armadilhas: canonical autorreferenciado, hreflang e 404 de verdade
Canonical. Com duas formas possíveis de acessar a mesma coisa, o canonical autorreferenciado em cada página resolve qualquer ambiguidade. Cada HTML pré-renderizado deve apontar para a sua própria URL limpa, com a mesma forma que você usa no sitemap: mesmo protocolo, mesmo domínio, com ou sem barra no final, sempre igual.
hreflang. Se o site tem versão em outro idioma, como /blog/artigo e /en/blog/artigo, cada versão precisa declarar todas as alternativas, incluindo ela mesma. Marcação de idioma feita apenas por JavaScript costuma não sobreviver ao HTML bruto, então ela precisa entrar na pré-renderização.
404 de verdade. Este é o erro mais comum e o mais invisível. Um SPA responde 200 para qualquer caminho, inclusive para os que não existem, e mostra uma tela de erro no navegador. Para o Google, isso é um soft 404: uma página que diz estar tudo certo mas não tem conteúdo válido. O servidor precisa devolver status 404 de verdade para rotas inexistentes, com ErrorDocument apontando para a sua página de erro.
Se você já tem tráfego e vai mexer nessa estrutura, leia antes como fazer um redesign sem perder SEO. E se quiser ver o resultado dessa abordagem em sites reais, o nosso portfólio mostra projetos construídos assim.
Se o seu site em React está invisível para o Google e você quer entender exatamente onde está o gargalo, fale com a ALB Seven. A gente faz esse diagnóstico com frequência.