SEO para SPA
Como tornar aplicações de página única (React Router, Vue Router, Angular Router) rastreáveis e indexáveis — os problemas de app-shell e soft-404, History API vs roteamento por hash, HTML por rota via SSR/prerender, canonicals e títulos por rota, e geração de sitemap para rotas no lado do cliente.
Idiomas
Uma aplicação de página única carrega um documento e troca as visualizações com JavaScript em vez de solicitar uma nova página do servidor. Muitas SPAs implementam isso com um 'app shell' — o servidor retorna HTML real de uma URL, uma casca quase vazia, até que o JS renderize cada rota — mas isso é uma escolha comum de implementação, não uma regra que toda SPA segue; verifique o que uma solicitação direta a cada rota realmente retorna antes de assumir. A correção, quando um setup de app-shell vazio é o problema, tem duas metades independentes: endereçabilidade (History API, não fragmentos hash/#!, para que cada visualização tenha uma URL real — uma navegação suave do navegador via History API muda a URL e a interface, mas não cria por si só uma nova resposta do servidor) e disponibilidade de conteúdo (SSR, prerender ou um meta-framework para que cada uma dessas URLs possa retornar HTML exclusivo sob solicitação). Além disso, verifique o título, canonical e estado de robots de cada rota tanto na entrada direta quanto após a renderização, lide com soft 404s (os roteadores tendem a manter um status 200 para visualizações 'não encontrado' — redirecione para uma URL que realmente retorne 404, ou adicione um noindex renderizado, embora um noindex inicial possa fazer com que a renderização seja ignorada), e garanta que cada rota no seu sitemap realmente resolva para HTML real e indexável — não há formato especial de sitemap para SPA.
TL;DR — Um aplicativo de página única (SPA) carrega uma página do servidor e depois usa JavaScript para alternar “páginas” sem um recarregamento completo. O problema: muitos SPAs enviam a mesma página inicial quase vazia, independentemente de qual URL um mecanismo de busca solicita — isso é um risco comum de como os SPAs são geralmente construídos, não uma garantia de todo SPA. Para corrigir, certifique-se de que cada rota tenha sua própria URL real e seu próprio HTML real — geralmente renderizando páginas no servidor ou pré-construindo-as.
O que é um SPA
Um aplicativo de página única comumente atualiza visualizações e rotas no lado do cliente sem uma navegação completa de documento. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: MDN: Single-page application O Google pode renderizar SPAs com JavaScript, mas URLs rastreáveis, links, tratamento de status e conteúdo renderizado permanecem necessários. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: JavaScript SEO basics
Um aplicativo de página única é um site construído como uma única página HTML. Quando você clica — da página de produtos para a página sobre — o JavaScript troca o que está na tela em vez de pedir ao servidor uma página inteira nova. React (com React Router), Vue (com Vue Router) e Angular funcionam assim por padrão. Parece rápido e semelhante a um aplicativo, por isso é popular.
O risco é o que o servidor envia. Muitos SPAs usam uma implementação de “app shell”:
a primeira vez que alguém (incluindo o Google) carrega o aplicativo, o servidor retorna um
shell quase vazio, e o conteúdo real é construído no navegador depois. Essa é
uma maneira comum de construir um SPA — não a definição de um, e não algo que todo
SPA faz. Mas quando um site envia um app shell vazio, o modo de falha é real: se
Google pedir ao servidor por /about e /products separadamente, ele pode obter o
mesmo shell vazio para ambos, porque nada sobre a rota mudou o que o servidor
enviou. A maneira de saber se seu aplicativo está nessa posição é verificar o que uma
solicitação direta a cada rota realmente retorna — não assumir isso pelo fato de ser um
SPA.
Por que isso prejudica o SEO
Os mecanismos de busca precisam ver seu conteúdo para ranqueá-lo. Com um SPA vazio, três coisas tendem a dar errado:
- Cada URL parece a mesma para o servidor. Links profundos, compartilhamentos e rastreadores todos caem no mesmo shell.
- Páginas 404 ainda dizem “200 OK”. Um roteador JavaScript pode mostrar uma tela de “não encontrado” enquanto a página tecnicamente relata sucesso, então o Google pode indexar páginas vazias.
- As URLs erradas. SPAs mais antigos usavam endereços como
example.com/#/products. O Google não consegue indexá-los de forma confiável.
Como corrigir (a versão curta)
- Dê a cada visualização uma URL real usando a API History do navegador (caminhos limpos como
/products), não os baseados em#. - Envie HTML real para cada URL. Renderize páginas no servidor (SSR) ou pré-construa-as antecipadamente (prerenderização). Se você está começando do zero, um framework que faz isso por você — Next.js, Nuxt, SvelteKit, SSR do Angular — poupa você do trabalho.
- Dê a cada página seu próprio título e descrição que mudam quando a rota muda.
- Torne os links links reais (
<a href>), não<div>s clicáveis.
Quer os mecanismos — por que o roteamento por hash falha, a armadilha da “diretiva mais restritiva” com canônicos e como gerar um sitemap para rotas do lado do cliente? Mude para a aba Avançado.
TL;DR — O risco central de SEO de um SPA não é “JavaScript” no abstrato — é que o roteamento do lado do cliente muda o que o usuário vê sem mudar o que o servidor retornaria. A correção tem duas metades independentes que as pessoas constantemente confundem: endereçabilidade (API History, URLs únicas por rota, sem fragmentos
#!) e disponibilidade de conteúdo (SSR, prerenderização/SSG ou um meta-framework). Faça apenas a primeira e você obtém um sitemap organizado de URLs que todas renderizam o mesmo shell. Além de ambas, cada rota precisa de seu próprio canônico/título/descrição no DOM renderizado, você tem que lidar com 404s suaves que mantêm um200, e cada rota no sitemap tem que resolver independentemente para HTML real.
O que realmente é SEO para SPA
SPA é uma arquitetura de aplicação, não um estado de falha garantido em SEO. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: MDN: Single-page application Renderização no servidor, pré-renderização ou renderização no cliente cuidadosamente implementada podem expor conteúdo, mas nenhuma garante indexação. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: JavaScript SEO basics
Este é o mergulho profundo em aplicações de página única especificamente — roteamento no lado do cliente com React Router, Vue Router ou Angular Router, onde após o primeiro carregamento o servidor nunca mais envia uma página completa. Ele acompanha o guia mais amplo de JavaScript SEO (que cobre paridade, carregamento preguiçoso, rolagem infinita e renderização de JS em geral) e os artigos específicos por framework para React, Next.js, Nuxt, Angular, Vue, Svelte e Astro. Aqui estou interessado apenas na camada de roteamento e no que ela faz com o rastreamento e a indexação.
Por que SPAs com roteamento no lado do cliente falham em SEO
Uma URL, uma resposta HTML — o problema do app shell
O Google descreve o modo de falha com precisão: “Some JavaScript sites may use the app
shell model where the initial HTML does not contain the actual content and Google needs
to execute JavaScript before being able to see the actual page content.” (tradução) «Alguns
sites com JavaScript podem usar o modelo de app shell em que o HTML inicial não contém o
conteúdo real e o Google precisa executar JavaScript antes de conseguir ver o conteúdo real
da página.» Em uma SPA pura, o “app shell” é tudo o que o servidor retorna. Busque
/products do zero e você obtém o mesmo documento quase vazio que obteria para /about.
O conteúdo só diverge depois que o navegador executa seu JavaScript e seu roteador decide
o que mostrar.
Esse é o problema inteiro em uma frase: o roteamento no lado do cliente muda o que o usuário vê sem mudar o que o servidor retornaria. Tudo a jusante — 404s suaves, conteúdo duplicado, títulos ausentes — é um sintoma disso.
O servidor nunca vê qual “página” foi solicitada (por que o roteamento por hash falha)
SPAs mais antigas carregam visualizações a partir de fragmentos de URL —
example.com/#/products. As palavras exatas do Google: “A SPA may use URL fragments
(for example https://example.com/#/products) for loading different views.” (tradução)
«Uma SPA pode usar fragmentos de URL (por exemplo https://example.com/#/products) para
carregar diferentes visualizações.» Isso falha por um motivo específico e mecânico:
os navegadores nunca enviam o fragmento (qualquer coisa após #) ao servidor na
solicitação HTTP. O servidor literalmente não consegue saber qual “página” foi
solicitada, então não pode retornar conteúdo diferente ou um código de status diferente
para ela. Um rastreador que busca a URL uma vez obtém HTML idêntico, independentemente
do fragmento.
É também por isso que o Google descontinuou formalmente seu esquema de rastreamento AJAX
de 2009 em outubro de 2015: “In short: We are no longer recommending the AJAX crawling
proposal we made back in 2009.” (tradução) «Em resumo: não estamos mais recomendando
a proposta de rastreamento AJAX que fizemos em 2009.» A antiga solução alternativa
_escaped_fragment_ permitia que servidores pré-renderizassem rotas de fragmento sob
solicitação — mas ela contornava o problema de roteamento em vez de corrigi-lo. A própria
recomendação do Google que a substitui é a History API, abordada abaixo.
404s suaves — roteadores no lado do cliente mantêm 200 para tudo
Isso é quase inevitável em uma SPA pura. Roteadores no lado do cliente, por design,
mantêm o status 200 da página original para cada navegação virtual — incluindo estados
de “não encontrado”. O Google sinaliza isso explicitamente: “In a single-page application
(SPA), this can be especially difficult. To prevent error pages from being indexed, you
can use one or both of the following strategies.” (tradução) «Em uma aplicação de
página única (SPA), isso pode ser especialmente difícil. Para evitar que páginas de erro
sejam indexadas, você pode usar uma ou ambas as estratégias a seguir.» E o porquê:
“When a SPA is using client-side JavaScript to handle errors they often report a 200
HTTP status code instead of the appropriate status code.” (tradução) «Quando uma SPA
usa JavaScript no lado do cliente para lidar com erros, ela frequentemente reporta um
código de status HTTP 200 em vez do código de status apropriado.»
O resultado é que visualizações vazias ou de erro são indexadas como páginas 200 finas.
O Google documenta exatamente duas estratégias aqui, com precisão: redirecionar (ou fazer
uma solicitação completa) para uma URL cujo servidor retorna um 404/status de erro
real, ou adicionar uma tag noindex à visualização de erro com JavaScript. Preste atenção
na segunda: se esse noindex estiver presente desde a primeira pintura, em vez de ser
adicionado depois que o aplicativo decide que a rota é inválida, isso pode fazer o Google
pular a renderização da página por completo — então verifique o que uma solicitação direta
retorna antes de qualquer JS ser executado, não apenas o que aparece no DOM renderizado
depois. Evidence for this claim For a client-rendered not-found view, Google documents two approaches: redirect to a URL whose server returns a 404 response, or add noindex with JavaScript; an initial noindex can cause rendering to be skipped, so raw and rendered directives require separate testing. Scope: SPAs Confidence: high · Verified: Fix Search-related JavaScript problems
Duplicatas de shell compartilhado — uma causa possível, não um diagnóstico automático
Existe um segundo modo de falha mais sorrateiro: rotas distintas são indexadas como duplicatas umas das outras porque renderizam o mesmo boilerplate compartilhado de cabeçalho/navegação/rodapé. Gary Illyes descreveu uma forma como isso acontece: “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out (my most likely explanation) and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.” (tradução) «Tenho um monte de e-mails na minha caixa de entrada onde o problema é que a peça central demorou muito para carregar, então a renderização expirou (minha explicação mais provável) e ficamos com um monte de páginas que só tinham o boilerplate. Com apenas o boilerplate, essas páginas são duplicatas.» A correção dele: “Try to restructure the js calls such that the content (including marginal boilerplate) loads first.” (tradução) «Tente reestruturar as chamadas de js para que o conteúdo (incluindo o boilerplate marginal) carregue primeiro.» (Transmitido via uma postagem no LinkedIn, que resiste à verificação automatizada; trate como fonte secundária de alta confiança.)
Observe que Illyes o enquadra como “minha explicação mais provável”, não um diagnóstico confirmado — vale a pena levar isso ao pé da letra. “Tempo limite de renderização” não é algo que você possa diagnosticar apenas pelos sintomas; você precisa de evidências no nível de requisição (o que uma busca direta realmente retorna), evidências de saída renderizada (se o conteúdo da peça central está presente após a renderização) e evidências do Search Console (sinais de duplicata/indexação) antes de atribuir o problema de conteúdo duplicado de uma rota a um tempo limite especificamente, em vez de a um bug, a um recurso bloqueado ou a um shell genuinamente idêntico. Também não há um tempo limite fixo e publicado para projetar em torno dele — o próprio documento básico do Google diz que uma página “may stay on this queue for a few seconds, but it can take longer than that” (tradução) «pode permanecer nesta fila por alguns segundos, mas pode levar mais tempo do que isso», sem se comprometer com um número. Não planeje em torno de uma duração assumida da fila de renderização; em vez disso, garanta que o conteúdo principal carregue o mais cedo possível, independentemente de quanto tempo a renderização levar.
A correção: obter HTML real por rota
A correção completa tem duas partes independentes que as pessoas confundem constantemente:
- Endereçabilidade de URL — API History, URLs únicas por rota, sem fragmentos.
- Disponibilidade de conteúdo — SSR, pré-renderização estática/SSG ou um meta-framework.
Fazer apenas o nº 1 e você obtém URLs limpas e compartilháveis que ainda retornam o mesmo shell em branco. Para uma rota que você realmente deseja indexar, você precisa de ambos.
Isso não é um mandato universal para adicionar SSR em todos os lugares, no entanto. SSR e pré-renderização reduzem o quanto você depende de um crawler executar seu JavaScript com sucesso — eles não se tornam obrigatórios no instante em que um site é tecnicamente um SPA. Antes de escolher uma arquitetura para uma determinada rota, verifique três coisas: se essa rota precisa ranquear de alguma forma (um painel administrativo interno não precisa), o que uma requisição direta e sem JS a ela já retorna (algumas configurações já enviam HTML significativo) e quais crawlers você realmente precisa satisfazer (o Google renderiza JS de forma bastante confiável; o Bing e a maioria dos crawlers de IA menos — veja abaixo). CSR que já passa nessas verificações não precisa se tornar SSR apenas porque o site é um SPA.
Renderização no servidor (SSR)
O servidor executa seu aplicativo para cada requisição e retorna HTML totalmente formado para essa rota, então o cliente o “hidrata” em um SPA vivo. Esta é a opção mais robusta porque o crawler obtém conteúdo completo na primeira busca, sem necessidade de execução de JS.
Pré-renderização estática / SSG
Em vez de renderizar por requisição, você constrói o HTML de cada rota com antecedência no deploy. Perfeito para conteúdo que não muda por usuário. Da minha própria escrita sobre SEO de JavaScript: qualquer tipo de SSR, renderização estática e configuração de pré-renderização vai ser bom para mecanismos de busca — a coisa a evitar é deixar o conteúdo bloqueado atrás de renderização somente no cliente.
Ou: não faça manualmente — use um meta-framework
Para uma nova construção, a recomendação honesta é não criar roteamento do lado do cliente apenas com React-Router ou Vue-Router. Um meta-framework — Next.js, Nuxt, Angular com seu pacote SSR, SvelteKit, Remix — oferece SSR/SSG e metadados baseados em rotas prontos para uso, o que contorna toda essa categoria de problema. (Cada um deles tem seu próprio mergulho profundo neste site.) Seja honesto sobre o outro lado: adaptar SSR a um SPA existente feito à mão é um trabalho real de engenharia — um projeto de migração, não uma alternância de configuração.
Renderização dinâmica como paliativo, não como destino
Você pode servir aos rastreadores um snapshot HTML renderizado separadamente (renderização dinâmica). Tanto o Google quanto o Bing aceitam — o Bing “recomenda a renderização dinâmica como uma ótima alternativa para sites que dependem muito de JavaScript” (transmitido do blog de 2018 do Bing; as duas citações curtas sobre capacidades do bingbot abaixo são verificadas diretamente, esta mais longa não foi rechecada de forma independente) — mas o Google deixa claro que é um paliativo: “A renderização dinâmica era um paliativo e não uma solução de longo prazo… Em vez disso, recomendamos que você use renderização no servidor, renderização estática ou hidratação como solução.” (Transmitido do documento de renderização dinâmica do Google; a redação corresponde ao que já está citado no guia mais amplo de SEO para JavaScript deste site.) É uma ponte, não uma arquitetura.
API History vs. roteamento por hash (#!)
Cinco estados, não dois
Testar uma rota de SPA fica confuso porque “funciona” na verdade abrange cinco estados diferentes e verificáveis separadamente:
| Estado | O que é |
|---|---|
| Resposta do servidor | Os bytes que uma solicitação HTTP nova, sem JavaScript, a uma URL realmente recebe de volta. |
| DOM renderizado | O que um navegador (ou o renderizador do Googlebot) constrói após executar JavaScript contra essa resposta do servidor. |
| Processamento de busca | Como o Google rastreia separadamente a resposta do servidor, depois renderiza a página e indexa com base em ambos. |
| Navegação de documento completo no navegador | Uma solicitação HTTP nova e real a uma URL — o único estado que pode alterar a resposta do servidor ou o código de status. |
| Navegação suave no navegador | Uma transição da API History (pushState/replaceState) que altera a URL visível, o histórico do navegador e a interface na tela. |
A que todos confundem: uma navegação suave altera a URL e a interface, mas não cria por si só uma nova resposta HTTP ou status — isso só acontece em uma navegação completa (ou em uma solicitação direta equivalente, como curl). Testar uma rota clicando pelo aplicativo a partir da página inicial exercita a navegação suave; testá-la solicitando a URL diretamente exercita a resposta do servidor. Ambos importam, e podem discordar.
O que a API History oferece
A recomendação do Google é inequívoca: “Recomendamos usar a API History para carregar conteúdo diferente com base na URL em um SPA.” (No documento ativo, “History API” é um link, então este link profundo tem como alvo a cláusula introdutória.) A API History (pushState/replaceState) permite que seu roteador altere a URL visível para um caminho real e marcável — /products, não /#/products — sem um recarregamento completo. Isso corrige a metade de endereçabilidade: agora cada visualização tem uma URL à qual um servidor poderia responder de forma diferente.
Por que URLs de fragmento/hash são invisíveis para o servidor — e para o Google
Como o fragmento nunca chega ao servidor (veja acima), a API History é a única maneira de dar a cada rota uma URL que o servidor possa realmente servir. O Google estabelece um piso em seu documento básico: “não use fragmentos para carregar conteúdo de página diferente. O exemplo a seguir é uma prática ruim, porque o Googlebot não consegue resolver as URLs de forma confiável.” Esta é a mesma orientação que escrevi em outros lugares — use URLs de aparência normal como /products, não URLs de hash como /#/products, porque o Google não consegue indexar as de hash de forma confiável.
A depreciação de 2015, brevemente
A era do hash-bang (#!) terminou com o post de 2015 do Google: “Times have changed. Today, as
long as you’re not blocking Googlebot from crawling your JavaScript or CSS files, we are
generally able to render and understand your web pages like modern browsers,” e
“you can use the History API pushState() to ensure accessibility for a wider range of
browsers (and our systems).” Uma nuance que vale manter: o Google não desindexou
instantaneamente os sites antigos com hash-bang — “we’ll generally crawl, render, and index the #! URLs” — mas
“we can still try” não é “you should still do this.”
Tornando cada rota indexável de forma independente
URLs limpas e HTML real fazem você ser rastreado. Para ser indexado corretamente, cada rota precisa de seus próprios sinais.
Tags canônicas por rota — e a armadilha da “diretiva mais restritiva”
Cada rota precisa de seu próprio rel=canonical no DOM renderizado. A armadilha: se um
canonical de espaço reservado (ou um noindex) for enviado no shell HTML bruto e o JavaScript
for sobrescrevê-lo depois, você pode ter um conflito. O Google resolve conflitos
entre as versões bruta e renderizada escolhendo o sinal mais restritivo — como eu já
disse antes, o Google escolherá as declarações mais restritivas entre o HTML e
a versão renderizada de uma página. Um noindex perdido ou um canonical errado embutido no
shell pode suprimir silenciosamente a rota inteira mesmo depois que seu JS a “corrige”.
Títulos e meta descrições por rota via JavaScript
Definir isso em JS é aceitável — o Google diz isso diretamente: “You can use JavaScript to set
or change the meta description as well as the <title> element.” O requisito é
que eles cheguem ao DOM renderizado que o Google avalia, não apenas pisquem brevemente. Dê a cada
página seu próprio título e descrição que se atualizem quando a rota mudar.
Links reais <a href> entre rotas
Torne seus links entre rotas âncoras reais com atributos href, não manipuladores de clique em
<div>s. O Google descobre URLs extraindo hrefs; um <div onClick> que navega
via roteador é invisível para a extração de links do rastreador. E não dependa de
estado no lado do cliente para carregar conteúdo nessas navegações — o renderizador do Google “does
not retain state across page loads: Local Storage and Session Storage data are cleared
across page loads. HTTP Cookies are cleared across page loads.”
Geração de sitemap para rotas SPA
Não existe um formato especial de “sitemap SPA” — é um problema de contabilidade
O protocolo de sitemap não muda para SPAs. O trabalho real é garantir que cada rota que você lista de forma independente resolva para HTML real, único e renderizado. Um sitemap de 500 rotas no lado do cliente é inútil se essas rotas todas retornarem o mesmo shell. Então o sitemap é consequência da sua estratégia de renderização, não um substituto para ela.
Lidando com rotas dinâmicas/parametrizadas (/product/:id)
Para aplicativos com rotas parametrizadas, você não pode manter a lista manualmente. O gerador
de sitemap tem que rodar contra a mesma fonte de dados que o aplicativo usa — um script de build ou um
endpoint de servidor que enumera cada id — para que o sitemap e o aplicativo nunca se
distanciem. Essas URLs enumeradas só valem a pena listar depois que forem SSR’d ou pré-renderizadas.
Mantendo o sitemap sincronizado com o que é resolvível no servidor
Regenere o sitemap como parte do seu build ou em um cronograma vinculado à sua fonte
de conteúdo. Uma rota que retorna 404 (ou pior, soft-404 em 200) mas está no seu sitemap é um
sinal de orçamento de rastreamento e qualidade que você não quer.
Testando o que o Google realmente vê
Não verifique apenas uma URL. O ponto central do problema SPA é que as rotas podem diferir no navegador, mas não na rede, então teste várias rotas no nível do HTML bruto:
- Busque o HTML bruto por rota com
curl(um user-agent do Googlebot quando relevante) e confirme que o conteúdo é único por URL, não o shell compartilhado. - Inspeção de URL no Search Console — compare o HTML rastreado/renderizado de várias rotas e confirme que o título, a descrição e o canônico por rota estão presentes.
- Confirme que as rotas de erro retornam o sinal correto — uma visualização de “não encontrado” deve
redirecionar para um status de erro real ou carregar
noindexno DOM renderizado.
Mitos comuns sobre SEO de SPA
- “O Google não consegue indexar SPAs de forma alguma.” Desatualizado. O Google executa Chromium evergreen e geralmente renderiza conteúdo de SPA. Os riscos reais são específicos: soft 404s, roteamento por hash, timeouts de renderização e rastreadores que não são do Google (Bing com menos confiabilidade, a maioria dos rastreadores de IA nem sequer).
- “Adicionar a History API corrige o SEO de SPA.” Isso corrige apenas a endereçabilidade. Se o servidor ainda retorna o mesmo shell para cada rota, o Google ainda precisa executar JS para ver qualquer coisa.
- “O roteamento por hash ainda funciona como fallback.” Descontinuado desde 2015 e recomendado contra desde então.
- “Renderização no cliente é uma penalidade de ranqueamento.” Não existe penalidade direta de CSR. O dano é indireto — renderização falha/atrasada, soft 404s e agrupamento duplicado de timeouts de renderização reduzem o que é indexado.
- “Pré-renderização para bots é cloaking.” Não quando o conteúdo corresponde ao que os usuários eventualmente veem — apenas o quando/onde da renderização difere, não o conteúdo.
- “Você precisa de um gerador de sitemap especial para SPA.” Não existe formato especial; o trabalho é fazer cada rota listada resolver para HTML real.
Perguntas frequentes
O Google consegue indexar uma aplicação de página única? Sim, se cada rota resolver para HTML real e único (via SSR/pré-renderização) em uma URL real. SPAs apenas com cliente muitas vezes não conseguem.
Preciso de SSR, ou a renderização no cliente é aceitável em algum caso? CSR pode funcionar para conteúdo que não precisa ranquear, mas para qualquer coisa que você queira indexar de forma confiável, pré-renderize ou use SSR — não aposte que o renderizador executará seu JS a tempo.
O roteamento por hash (#!) é ruim para SEO? Sim — o fragmento nunca chega ao servidor, então
não é possível resolver essas URLs de forma confiável. Use a History API.
Toda rota precisa de sua própria tag canônica? Sim, no DOM renderizado — e certifique-se
que nada mais restritivo (um noindex perdido ou canônico errado) seja enviado no shell
bruto.
Um serviço de pré-renderização é cloaking? Não, desde que o conteúdo servido aos bots corresponda ao que os usuários veem.
Devo usar um meta-framework em vez de construir o roteamento eu mesmo? Para um novo build, geralmente sim — Next.js/Nuxt/Angular-SSR/SvelteKit oferecem SSR/SSG e metadados por rota de graça.
Por que meu SPA retorna 200 para páginas que não existem? Porque o roteador
no cliente mantém o status 200 original para navegações virtuais. Corrija com um
redirecionamento JS para um status de erro real ou um noindex renderizado.
Resumo de IA
Uma visão condensada da versão Avançada:
- SEO para SPA = roteamento no lado do cliente especificamente (React Router, Vue Router, Angular Router). Uma SPA é definida por carregar um documento e trocar visualizações com JavaScript — um “app shell” que envia HTML quase vazio para cada URL é uma implementação comum, não uma regra que toda SPA segue; verifique o que uma solicitação direta realmente retorna em vez de presumir.
- O risco central: o roteamento no lado do cliente pode mudar o que o usuário vê sem
mudar o que o servidor retornaria. Em uma SPA de app shell puro, buscar
/productse/aboutdiretamente pode obter HTML bruto byte idêntico. - Cinco estados são confundidos: resposta do servidor, DOM renderizado, processamento do Search, navegação de documento completo do navegador e navegação suave do navegador. Uma transição da History API muda a URL e a interface, mas não cria por si só uma nova resposta do servidor ou status.
- Três sintomas de um shell vazio: o problema do app shell, soft 404s (roteadores mantêm um
200para “não encontrado” — as duas correções documentadas do Google são redirecionar para uma URL que ela mesma retorna 404, ou umnoindexadicionado via JS; umnoindexinicial pode fazer com que a renderização seja ignorada) e duplicatas de shell compartilhado — das quais o “tempo limite de renderização” (Illyes) é uma causa possível, não um diagnóstico automático sem evidências de solicitação/renderização/Search. - Roteamento por hash falha mecanicamente: o fragmento após
#nunca chega ao servidor, então o servidor não pode diferir por rota. Depreciado pelo Google em 2015; a History API dá endereçabilidade, não uma correção completa de indexabilidade. - A correção tem duas metades independentes quando um shell vazio é o problema: endereçabilidade (History API, URLs únicas por rota) e disponibilidade de conteúdo (SSR, pré-renderização/SSG, ou um meta-framework) — mas SSR não é um requisito universal; baseie a decisão em se a rota precisa ranquear, o que ela já retorna diretamente e quais rastreadores precisam renderizá-la.
- Por rota: tenha canonical/título/descrição próprios no DOM renderizado; cuidado com a
armadilha da “diretiva mais restritiva” onde um
noindex/canonical de HTML bruto substitui seu JS. Links reais<a href>; não dependa do estado do cliente (WRS limpa armazenamento/cookies entre carregamentos). - Sitemaps: listar uma rota não prova que ela resolve — não existe formato especial; cada rota listada deve resolver independentemente para HTML real, e rotas dinâmicas precisam de um gerador vinculado à fonte de dados do aplicativo.
- Renderização dinâmica é um paliativo aceito pelo Google/Bing, não uma arquitetura de longo prazo.
- Teste várias rotas no nível de HTML bruto (solicitações diretas), não apenas navegando dentro do aplicativo.
Documentação oficial
Documentação de fonte primária dos mecanismos de busca.
- Entenda os fundamentos de SEO para JavaScript — o modelo de app shell, “Use a History API em vez de fragmentos,” e definir títulos/descrições via JS.
- Corrija problemas de JavaScript relacionados à Pesquisa — a seção de soft 404 para SPAs, “Não use fragmentos de URL para carregar conteúdo diferente,” e a recomendação da History API.
- Renderização dinâmica como solução alternativa — por que a renderização dinâmica é um paliativo, não uma solução de longo prazo.
- Depreciação do nosso esquema de rastreamento AJAX (2015) — o fim formal do rastreamento com hash-bang e a recomendação de pushState.
- Crie e envie um sitemap — protocolo geral de sitemap (não há formato específico para SPA).
Bing / Microsoft
- Série bingbot: JavaScript, renderização dinâmica e cloaking. Oh, meu Deus! — as capacidades de renderização de JS do bingbot e sua postura sobre renderização dinâmica.
Citações da fonte
Declarações oficiais do Google e do Bing. Cada link é um link profundo que salta para a passagem citada na página de origem.
Google — o problema do app shell
- “Some JavaScript sites may use the app shell model where the initial HTML does not contain the actual content and Google needs to execute JavaScript before being able to see the actual page content.” (tradução) «Alguns sites JavaScript podem usar o modelo de app shell, em que o HTML inicial não contém o conteúdo real e o Google precisa executar JavaScript antes de conseguir ver o conteúdo real da página.» Ir para a citação
Google — roteamento por hash/fragmento e a History API
- “A SPA may use URL fragments (for example https://example.com/#/products) for loading different views.” (tradução) «Uma SPA pode usar fragmentos de URL (por exemplo, https://example.com/#/products) para carregar diferentes visualizações.» Ir para a citação
- “We recommend using the History API to load different content based on the URL in a SPA.” (tradução) «Recomendamos usar a History API para carregar conteúdo diferente com base na URL em uma SPA.» Ir para a citação (No documento ao vivo, “History API” é um link, então este link profundo aponta para a cláusula introdutória; a frase completa está presente literalmente em ordem.)
- “don’t use fragments to load different page content. The following example is a bad practice, because Googlebot can’t reliably resolve the URLs.” (tradução) «não use fragmentos para carregar conteúdo de página diferente. O exemplo a seguir é uma prática ruim, porque o Googlebot não consegue resolver as URLs de forma confiável.» Ir para a citação
Google — soft 404s em SPAs
- “In a single-page application (SPA), this can be especially difficult. To prevent error pages from being indexed, you can use one or both of the following strategies.” (tradução) «Em uma aplicação de página única (SPA), isso pode ser especialmente difícil. Para evitar que páginas de erro sejam indexadas, você pode usar uma ou ambas as estratégias a seguir.» Ir para a citação
- “When a SPA is using client-side JavaScript to handle errors they often report a
200HTTP status code instead of the appropriate status code.” (tradução) «Quando uma SPA usa JavaScript no lado do cliente para lidar com erros, ela frequentemente reporta um código de status HTTP200em vez do código de status apropriado.» Ir para a citação
Google — meta tags via JavaScript e renderização sem estado
- “You can use JavaScript to set or change the meta description as well as the
<title>element.” (tradução) «Você pode usar JavaScript para definir ou alterar a meta description, bem como o elemento<title>.» Ir para a citação - “WRS does not retain state across page loads: Local Storage and Session Storage data are cleared across page loads. HTTP Cookies are cleared across page loads.” (tradução) «O WRS não mantém estado entre carregamentos de página: os dados de Local Storage e Session Storage são limpos entre carregamentos de página. Os cookies HTTP são limpos entre carregamentos de página.» Ir para a citação
Google — descontinuação do rastreamento hash-bang (2015)
- “In short: We are no longer recommending the AJAX crawling proposal we made back in 2009.” (tradução) «Em resumo: não estamos mais recomendando a proposta de rastreamento AJAX que fizemos em 2009.» Ir para a citação
- “Times have changed. Today, as long as you’re not blocking Googlebot from crawling your JavaScript or CSS files, we are generally able to render and understand your web pages like modern browsers.” (tradução) «Os tempos mudaram. Hoje, desde que você não esteja bloqueando o Googlebot de rastrear seus arquivos JavaScript ou CSS, geralmente conseguimos renderizar e entender suas páginas da web como navegadores modernos.» Ir para a citação
Google — renderização dinâmica é um workaround (relatado; redação corresponde ao que já está citado no guia mais amplo de SEO para JavaScript deste site, não verificado de forma independente novamente nesta passagem)
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” (tradução) «A renderização dinâmica foi um workaround e não uma solução de longo prazo para problemas com conteúdo gerado por JavaScript nos mecanismos de busca.» Ir para a citação
Bing — bingbot e JavaScript
- “bingbot is generally able to render JavaScript.” — bingbot Series, Bing Webmaster Blog. (tradução) «O bingbot geralmente consegue renderizar JavaScript.» Ler o post
- “bingbot does not necessarily support all the same JavaScript frameworks that are supported in the latest version of your favorite modern browser.” — mesmo post. (tradução) «O bingbot não suporta necessariamente todos os mesmos frameworks de JavaScript suportados na versão mais recente do seu navegador moderno favorito.» Ler o post
Gary Illyes, Google — timeouts de renderização criam duplicatas (relatado via um post no LinkedIn, que resiste à verificação automatizada; tratar como fonte secundária de alta confiança)
- “the centerpiece took forever to load, so rendering timed out… and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.” (tradução) «a peça central demorou uma eternidade para carregar, então a renderização expirou… e ficamos com um monte de páginas que só tinham o boilerplate. Com apenas o boilerplate, essas páginas são duplicatas.» Ler o post
Qual caminho de renderização devo seguir?
Trabalhe de cima para baixo. A pergunta é sempre “o crawler recebe HTML real para esta rota sem executar meu JS?” — e comece confirmando se a rota realmente precisa ser indexada e verificando o que uma solicitação direta, sem JS, já retorna; nem toda rota precisa de SSR, e algumas já retornam HTML utilizável.
1. Você está começando um novo build?
- Sim → Use um meta-framework (Next.js, Nuxt, Angular com SSR, SvelteKit, Remix). SSR/SSG e metadados por rota vêm embutidos. Pare aqui.
- Não → Continue.
2. Seu conteúdo muda por usuário / por solicitação?
- Não (conteúdo majoritariamente estático) → Prerender / SSG no momento do build. Correção mais simples e robusta — cada rota é HTML real no disco.
- Sim → Renderização no servidor (SSR) para que cada solicitação retorne HTML específico da rota.
3. Não consegue fazer SSR ou prerender agora (SPA legado feito à mão)?
- Use renderização dinâmica como uma ponte temporária — sirva aos crawlers um snapshot renderizado. Planeje a migração para SSR/SSG; não trate isso como o destino.
4. Qualquer que seja o caminho escolhido, confirme tudo isto por rota:
- URL real via History API (sem fragmentos
#!). - Título único + meta description + canonical no DOM renderizado.
- Nada mais restritivo (
noindex, canonical errado) embutido no shell bruto. - Links reais
<a href>entre rotas. - Rotas de erro retornam um status de erro real ou um
noindexrenderizado (sem soft 404s). - Cada rota no sitemap resolve de forma independente para HTML real.
Checklist de SEO para SPA
Endereçabilidade
- Cada visualização tem uma URL real via History API (
/products), não um fragmento (/#/products). - Links entre rotas são âncoras reais
<a href>, não handlers<div onClick>.
Disponibilidade de conteúdo
- Cada rota retorna HTML real e único sem que o crawler execute seu JS (SSR, prerendering/SSG ou um meta-framework).
- O conteúdo da rota carrega antes de a janela de renderização fechar (evite duplicatas apenas com boilerplate).
Indexabilidade por rota
-
<title>e meta description exclusivos por rota, presentes no DOM renderizado. -
rel=canonicalexclusivo por rota no DOM renderizado. - Nenhum
noindex/canônico de espaço reservado no shell bruto que um sinal mais restritivo possa fixar.
Erros
- Rotas “não encontradas” redirecionam para uma URL cujo servidor retorna um status de erro real ou
carregam
noindexadicionado por JavaScript (sem soft 404 em200). - Se usar a rota
noindex, confirme que ela é adicionada depois que o aplicativo decide que a rota é inválida, não presente desde a primeira pintura — umnoindexinicial pode fazer o Google pular a renderização da página.
Sitemap
- Cada rota listada resolve independentemente para HTML real.
- Rotas dinâmicas (
/product/:id) enumeradas a partir da fonte de dados do aplicativo, não mantidas manualmente.
Verificação
- HTML bruto verificado em várias rotas (curl), confirmando conteúdo exclusivo por URL.
- URL Inspection confirma conteúdo renderizado, título, descrição e canônico por rota.
Os modelos mentais
1. As duas metades que você não deve confundir. Endereçabilidade (History API, URLs exclusivas, sem fragmentos) e disponibilidade de conteúdo (SSR, prerenderização ou um meta-framework) são independentes. URLs limpas sem HTML por rota = um sitemap organizado de shells vazios. Você precisa de ambos.
2. “O que o servidor retornaria, fresco?”
Todo o problema do SPA é que a visão do navegador diverge da resposta do servidor. Para
qualquer rota, pergunte o que um curl sem execução de JS obtém. Se for o shell, o
crawler pode ver o shell também.
3. O padrão do código de status é 200 — inclusive para erros.
Roteadores do lado do cliente mantêm o 200 original. Assuma que toda visão de “erro” é um soft 404
até que você deliberadamente faça ela retornar um status de erro real ou um noindex renderizado.
4. A armadilha da diretiva mais restritiva.
O Google reconcilia bruto vs. renderizado tomando o sinal mais restritivo. Um noindex
ou canônico errado no shell pode sobrescrever seu “conserto” via JS. Audite o HTML bruto, não apenas
o DOM renderizado.
5. Meta-framework primeiro para novos builds. Fazer roteamento do lado do cliente manualmente significa ser dono de cada um desses problemas. Escolher um framework que faz SSR/SSG e metadados por rota é escolher não tê-los.
Folha de dicas de SEO para SPA
Roteamento
| Abordagem | Exemplo de URL | Chega ao servidor? | Seguro para o Google? |
|---|---|---|---|
| History API | /products | Sim | Sim (recomendado) |
| Roteamento por hash | /#/products | Não (fragmento nunca enviado) | Não — descontinuado em 2015 |
Hash-bang (#!) | /#!/products | Não | Não — esquema AJAX legado, descontinuado |
Estratégias de renderização
| Estratégia | O crawler obtém HTML real sem executar JS? | Melhor para |
|---|---|---|
| SSR | Sim | Conteúdo por usuário / por requisição |
| Prerenderização / SSG | Sim | Conteúdo majoritariamente estático |
| Meta-framework (Next/Nuxt/etc.) | Sim (integrado) | Novos builds |
| Renderização dinâmica | Sim, apenas para bots | Ponte temporária em SPAs legados |
| CSR puro (somente cliente) | Não | Nada que você queira indexar de forma confiável |
Obrigatórios por rota (todos no DOM renderizado)
- URL exclusiva (History API) ·
<title>exclusivo · meta description exclusiva ·rel=canonicalexclusivo · links<a href>reais · rotas de erro com status real ounoindex.
Fatos rápidos
- O fragmento após
#nunca é enviado ao servidor — é por isso que o roteamento por hash falha. - Roteadores do lado do cliente mantêm
200para tudo, incluindo “não encontrado” → soft 404s. - O Google reconcilia bruto vs. renderizado tomando o sinal mais restritivo.
- Não existe formato especial de sitemap para SPA — cada rota listada deve resolver para HTML real.
Anti-padrões de SEO para SPA
Enviar um SPA CSR puro e submeter um sitemap completo. O sitemap lista 500 rotas; todas as 500 retornam o mesmo shell para uma busca nova. O sitemap não faz o conteúdo existir — a renderização faz.
Adicionar a History API e considerar pronto. URLs limpas corrigem a endereçabilidade, não o conteúdo. Sem SSR/prerenderização, o servidor ainda retorna o shell para cada rota.
Hash / hash-bang routing “as a fallback.” O fragmento nunca chega ao servidor, então o servidor não pode diferenciar por rota. Obsoleto desde 2015; não é um fallback, é um beco sem saída.
Navegando com <div onClick> em vez de <a href>.
O Google extrai hrefs para descobrir URLs. A navegação por manipulador de clique é invisível para a extração
de links, então essas rotas podem nunca ser encontradas.
Um noindex ou canonical de espaço reservado no shell bruto “que o JS vai sobrescrever.”
O Google adota o mais restritivo entre bruto e renderizado. A diretiva do shell pode vencer e
suprimir silenciosamente a rota.
Retornando 200 para visualizações de “não encontrado”.
Soft 404s resultam em páginas finas/vazias indexadas. Redirecione para um status de erro real ou adicione um
noindex renderizado.
Carregando o conteúdo da rota lentamente, após o boilerplate. Timeouts de renderização deixam apenas o shell compartilhado, e cada rota colapsa em uma duplicata de todas as outras (Illyes). Carregue o conteúdo principal primeiro.
Dependendo do estado do cliente para transportar conteúdo entre navegações. O renderizador limpa Local/Session Storage e cookies entre carregamentos de página — conteúdo que só existe no estado do cliente não estará lá quando o Google renderizar a próxima rota.
Problemas comuns de indexação de SPA
Cada rota retorna o mesmo HTML
Sintoma: /products e /about têm visualizações diferentes no navegador, mas respostas brutas
idênticas. Causa provável: O roteamento por History API fornece endereçabilidade sem SSR
ou pré-renderização. Correção: Produza HTML específico por rota e confirme que uma solicitação direta a
cada caminho contém seu próprio título, corpo de texto e metadados.
Rotas ausentes aparecem como páginas bem-sucedidas
Sintoma: Uma rota inexistente mostra uma visualização de não encontrado enquanto retorna 200.
Causa provável: O roteador do cliente assume o erro depois que o servidor já enviou um
shell bem-sucedido. Correção: Retorne o status de servidor correto; se isso não puder ser lançado ainda,
redirecione para uma URL com um status de erro real ou renderize noindex. Confirme com uma
solicitação direta nova, não uma navegação no aplicativo.
Rotas colapsam em duplicatas após a renderização
Sintoma: Os mecanismos de busca agrupam URLs distintas ou retêm apenas a navegação compartilhada. Causa provável: O conteúdo da rota carrega tarde demais e a renderização captura o boilerplate. Correção: Priorize o conteúdo principal na resposta do servidor ou no caminho de renderização mais cedo. Confirme que várias rotas expõem conteúdo único antes que scripts opcionais terminem.
Testando o que o servidor realmente retorna por rota
A armadilha do SPA é que as rotas diferem no navegador, mas não no fio. Verifique várias rotas no nível de HTML bruto, não apenas uma.
Fetch raw HTML per route (macOS / Linux)
# Fetch a few routes with a Googlebot UA and compare — they should NOT be identical
UA="Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
for path in / /products /about /product/123; do
echo "=== $path ==="
curl -s -A "$UA" "https://example.com$path" | wc -c # byte counts should differ
doneCompare o HTML bruto de duas rotas (elas têm o mesmo shell?)
UA="Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
diff <(curl -s -A "$UA" https://example.com/products) \
<(curl -s -A "$UA" https://example.com/about) \
&& echo "IDENTICAL — bare shell, content is client-only" \
|| echo "Different — routes return distinct HTML (good)"Verifique o título e o canonical por rota na resposta bruta (grep / regex)
UA="Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
curl -s -A "$UA" https://example.com/products \
| grep -Eio '<title>[^<]*</title>|<link[^>]+rel=["'"'"']canonical["'"'"'][^>]*>'Check the HTTP status of a “not found” route (soft-404 detector)
# A missing route should NOT return 200
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/this-route-does-not-existIn the browser DevTools console — inspect the rendered title/canonical
// Run on each route after client-side navigation to confirm JS set them
console.log('title:', document.title);
console.log('description:',
document.querySelector('meta[name="description"]')?.content);
console.log('canonical:',
document.querySelector('link[rel="canonical"]')?.href);Bookmarklet — sinalize “links” com manipulador de clique que não são âncoras reais
javascript:(()=>{const bad=[...document.querySelectorAll('[onclick],div[role="link"]')]
.filter(e=>!e.closest('a[href]'));
bad.forEach(e=>e.style.outline='3px solid red');
alert(bad.length+' non-anchor clickable(s) outlined — these are invisible to link extraction');})();XPath — find fragment/hash links a crawler can’t resolve (paste into DevTools console)
$x('//a[starts-with(@href, "#") or contains(@href, "/#/")]')
.map(a => a.getAttribute('href'));
// Any results are hash-routed links; migrate them to History API paths. Ferramentas para auditar um SPA
- URL Inspection (Google Search Console) — veja o HTML rastreado vs. renderizado de uma rota e confirme se o title, a description e o canonical por rota realmente chegam.
- Rich Results Test / URL Inspection “View crawled page” — a própria renderização do Google de uma única URL, útil para confirmar que o conteúdo sobrevive à renderização.
curlcom um user agent do Googlebot — a forma mais rápida de comparar o HTML bruto de várias rotas e detectar um shell compartilhado.- Screaming Frog SEO Spider (modo de renderização JavaScript) — rastreie o site com e sem renderização para comparar conteúdo e titles brutos vs. renderizados em todas as rotas.
- Ahrefs Site Audit — revela problemas de indexabilidade, titles e canonicals ausentes/duplicados e padrões semelhantes a soft-404 nas rotas.
- DebugBear — monitoramento de desempenho voltado para SPA; o tempo de renderização importa porque rotas lentas podem expirar e virar duplicatas de boilerplate.
- Bing Webmaster Tools — URL Inspection — o bingbot renderiza JS de forma menos consistente que o Googlebot, então confirme suas rotas também no lado do Bing.
Prove que uma rota de SPA é indexável de forma independente
Teste a paridade de HTML em acesso direto
Teste a executar: Abra rotas representativas em uma nova sessão e busque as mesmas URLs
com curl. Resultado esperado: Cada URL retorna seu próprio conteúdo principal e sinais
de head sem estado prévio do aplicativo. Interpretação de falha: A rota depende de
navegação do cliente ou de estado armazenado. Janela de monitoramento: Imediata. Gatilho
de rollback: Uma rota funciona apenas ao entrar pela página inicial.
Teste o tratamento de status de erro
Teste a executar: Solicite uma rota conhecidamente inválida diretamente e inspecione tanto o status quanto as
diretivas renderizadas. Resultado esperado: Um status de erro genuíno, ou o fallback
documentado de um noindex renderizado/redirecionamento para uma resposta de erro. Interpretação
de falha: O SPA está gerando soft 404s. Janela de monitoramento: Imediata.
Gatilho de rollback: Caminhos inválidos são publicados como páginas 200 indexáveis.
Teste o isolamento de metadados
Teste a executar: Compare o title, a tag robots e o canonical brutos e renderizados em pelo menos três rotas. Resultado esperado: Cada rota tem um conjunto único, intencional e internamente consistente. Interpretação de falha: O shell compartilhado vaza metadados entre rotas ou o JS os sobrescreve tarde demais. Janela de monitoramento: Imediata localmente e após o recrawl no URL Inspection. Gatilho de rollback: Qualquer rota herda o canonical de outra rota ou uma diretiva restritiva do shell.
Teste-se: SEO para SPA
Cinco perguntas rápidas sobre como tornar aplicações de página única rastreáveis e indexáveis. Escolha uma resposta para cada uma e depois confira.
Recursos que valem seu tempo
Meus escritos relacionados
- JavaScript SEO Issues & Best Practices — meu guia completo de JS-SEO, incluindo as seções “don’t use fragments in URLs” e app-shell/duplicate-content nas quais este artigo se baseia.
- The Beginner’s Guide to Technical SEO — onde renderização e rastreamento se encaixam no panorama geral.
Minhas palestras
- How Search Works (SlideShare) — minha explicação sobre rastreamento, renderização, indexação e ranqueamento, que é o pipeline que um SPA precisa sobreviver. (Meu aviso padrão se aplica: “This is my understanding of systems… not going to be 100% complete or accurate.”) (tradução) «Esta é minha compreensão dos sistemas… não será 100% completa ou precisa.»
Da indústria em geral
- Google — Understand the JavaScript SEO basics — o modelo app-shell e “Use the History API instead of fragments.”
- Google — Fix Search-related JavaScript problems — a seção sobre soft-404 em SPAs e a recomendação da History API.
- Google — Deprecating our AJAX crawling scheme (2015) — o fim formal do rastreamento com hash-bang.
- Bing — bingbot Series: JavaScript, Dynamic Rendering, and Cloaking. Oh My! — a posição do bingbot sobre renderização de JS.
- It’s Not Cloaking To Use A Pre-Render Service For Blank HTML Pages For SPA Development (Search Engine Roundtable, 2015) — cobertura de Gary Illyes sobre pré-renderização de SPAs não ser cloaking. (Paráfrase do SER sobre Illyes, datada de 2015 — secundária.)
- SEO for Single Page Applications (Nuxt SEO) — uma explicação do lado do framework sobre os mesmos problemas.
- How To Optimize Single Page Applications For SEO (DebugBear) — o ângulo de renderização/desempenho na indexabilidade de SPAs.
- SPA (glossary) (MDN) — uma definição neutra do padrão de aplicação de página única.
Vídeos
- Google Search Central (YouTube) — a série sobre JavaScript SEO de Martin Splitt cobre renderização, a History API e os modos de falha de SPAs discutidos aqui. Canal
Registro de alterações
Atualizado em 18 de jul. de 2026.
Resumo editorial e detalhes registrados da alteração.Detalhes da alteração
-
As notas detalhadas sobre as alterações estão disponíveis atualmente em inglês.
-
As notas detalhadas sobre as alterações estão disponíveis atualmente em inglês.
-
As notas detalhadas sobre as alterações estão disponíveis atualmente em inglês.
-
As notas detalhadas sobre as alterações estão disponíveis atualmente em inglês.
-
As notas detalhadas sobre as alterações estão disponíveis atualmente em inglês.
Não é possível fazer a comparação completa — nenhum instantâneo anterior foi arquivado para esta revisão.