SEO para Angular
Como tornar aplicativos Angular rastreáveis e indexáveis com SSR, pré-rendering, renderização híbrida, hydration, meta tags e links reais.
Idiomas
SEO para Angular começa com HTML real na primeira resposta: use @angular/ssr ou pré-rendering em vez de um shell CSR vazio. Combine rotas pré-renderizadas e renderizadas no servidor com renderização híbrida, faça hydration do HTML do servidor e cuide de títulos, descrições, URLs History, links <a href> e JSON-LD. O Google consegue renderizar JavaScript, mas a renderização é enfileirada e outros rastreadores podem não executar JS; renderização dinâmica é uma solução provisória, não uma estratégia.
TL;DR — O Angular cria a página no navegador com JavaScript por padrão, então o HTML bruto que um mecanismo de busca vê primeiro está quase vazio. A correção é enviar HTML real e concluído — usando a renderização no servidor integrada ao Angular (
@angular/ssr) ou o pré-rendering. Depois cuide do básico: um título e uma descrição exclusivos em cada página, URLs limpas (sem#) e links reais<a href>.
O problema em uma frase
Um aplicativo Angular padrão envia ao navegador um pequeno shell HTML — basicamente uma <div> vazia — e um grande pacote de JavaScript. O navegador executa esse JavaScript e então a página é preenchida com conteúdo. Isso se chama renderização no lado do cliente (CSR).
O problema: quando um mecanismo de busca baixa essa URL pela primeira vez, ele vê o shell vazio. O Google pode executar o JavaScript para ver o conteúdo real, mas faz isso depois, em uma etapa separada, e nem sempre de forma confiável. Evidence for this claim Google can render JavaScript but processes rendering as a stage after crawling. Scope: Google Search rendering; other crawler behavior is not covered by this record. Confidence: high · Verified: Google: JavaScript SEO basics Outros rastreadores — o Bing e os bots por trás de prévias sociais — muitas vezes não conseguem executar JavaScript. Então não veem nada.
A correção: enviar HTML concluído
Em vez de fazer o navegador (ou o rastreador) construir a página, você a constrói antecipadamente ou em um servidor e envia o HTML completo. O Angular oferece duas maneiras principais:
- Renderização no servidor (SSR) — um servidor executa o Angular para cada solicitação e devolve a página completa. É boa para conteúdo que muda com frequência. Evidence for this claim Angular supports server-side rendering and build-time prerendering through its SSR tooling. Scope: Current Angular SSR and prerender features. Confidence: high · Verified: Angular: Server-side and hybrid rendering
- Pré-rendering — o Angular cria arquivos HTML estáticos para suas páginas durante o build, sem necessidade de servidor. É a opção mais rápida e ótima para posts de blog e páginas de marketing.
O Angular moderno (versão 17 em diante) já inclui os dois recursos no toolkit. Você os adiciona com um comando: ng add @angular/ssr. (Talvez você conheça o nome antigo Angular Universal — era a mesma ideia como um complemento separado. O Angular incorporou isso ao núcleo na v17 e mudou o nome.)
Os outros fundamentos
- Dê a cada página um título e uma descrição exclusivos. O Angular não fará isso por você — você os define no código usando os serviços integrados
TitleeMetado Angular. Sem isso, todas as páginas compartilham um título. - Use URLs limpas, não URLs com hash. O roteamento padrão do Angular cria URLs boas como
/products/shoes. Evite o estilo antigo com hash (/#/products) — os mecanismos de busca não conseguem tratar de modo confiável a parte depois do#. - Use links reais. A navegação precisa usar links reais
<a href>, não botões ou manipuladores de clique, ou o Google não conseguirá segui-los.
O que a maioria das pessoas entende errado
“O Google não consegue indexar Angular” é um mito — ele consegue renderizar JavaScript. Mas isso é mais lento e menos confiável do que simplesmente enviar HTML real, e rastreadores que não são do Google talvez não consigam fazer isso. Portanto, para qualquer conteúdo que você queira tornar encontrável, renderize-o no servidor ou faça seu pré-rendering.
Quer a versão aprofundada — renderização híbrida por rota, hydration, serviços de SEO com código e como testar o que o Google realmente vê? Mude para a aba Avançado.
Evidence for this claim Modern Angular documents @angular/ssr as its server-side and hybrid rendering package. Scope: Current Angular documentation; historical Angular Universal details are not required for this claim. Confidence: high · Verified: Angular: Server-side and hybrid renderingTL;DR — SEO para Angular é uma decisão arquitetural com várias funções: coloque HTML real na resposta em vez de um shell renderizado no cliente. O Angular moderno (v17+) incorpora SSR à CLI como
@angular/ssr(o sucessor integrado e renomeado do Angular Universal). Faça o pré-rendering de rotas estáticas, renderize rotas dinâmicas no servidor, combine as duas abordagens com renderização híbrida e faça hydration para que o HTML do servidor seja reutilizado, não reconstruído. Depois cuide do essencial: títulos e descrições exclusivos pelos serviçosTitle/Meta(ou pelaTitleStrategydo roteador), roteamento HTML5 History — nuncaHashLocationStrategy— links reais<a href>, JSON-LD injetado com segurança e verificação pelo URL Inspection. A renderização dinâmica é uma solução provisória reconhecida pelo Google, não uma estratégia.
O padrão é o problema: renderização no lado do cliente
Um build Angular comum envia um index.html cujo body é essencialmente <app-root></app-root> mais tags de script. Sem executar JavaScript, um rastreador não vê títulos, texto nem links — o conteúdo é montado no navegador depois que o pacote é carregado. Este é o mesmo problema central que descrevo em JavaScript SEO: a web deixou de usar apenas HTML, e CSR é a extremidade mais arriscada desse espectro.
(Os detalhes de implementação deste guia — APIs RenderMode, gatilhos de hydration e replay de eventos — refletem o Angular v22. Os recursos específicos de versão abaixo são datados: renomeação do SSR na v17, replay de eventos na v18 e hydration incremental nas v19–v20.)
CSR cria três problemas distintos de SEO:
- Indexação atrasada e menos confiável. O Google processa aplicativos JS em três fases — “Crawling, Rendering, and Indexing” (tradução) «rastreamento, renderização e indexação» — e a renderização é adiada para uma fila. Seu conteúdo não existe para indexação até que essa renderização aconteça.
- Core Web Vitals piores. O LCP sofre porque o navegador precisa baixar e executar um pacote antes de pintar conteúdo significativo.
- Rastreadores que não são do Google veem o shell. O Bingbot é muito menos consistente ao renderizar JS, e bots de prévias sociais/de links geralmente não renderizam nada — portanto, tags Open Graph e conteúdo injetado no cliente nunca chegam a eles.
Como o Googlebot realmente trata um aplicativo Angular
O Googlebot é evergreen — renderiza com uma versão atual do mecanismo V8 do Chrome e se atualiza junto com os lançamentos do Chrome. Evidence for this claim Googlebot uses an evergreen version of Chromium for rendering. Scope: Google Search rendering engine; this does not remove application-level rendering risks. Confidence: high · Verified: Google: Evergreen Googlebot Então ele consegue executar Angular. Mas o comportamento tem limites concretos que vale considerar no design:
- A renderização entra em uma fila; não é imediata. O Google documenta fases distintas de rastreamento, renderização e indexação, mas não publica um prazo fixo para a etapa de renderização alcançar o rastreamento. O resumo usado pelo setor é “duas ondas” — o HTML bruto é indexado primeiro (um shell vazio para Angular em CSR), e o DOM renderizado é indexado depois, quando essa renderização acontece. SSR/pré-rendering contorna a espera: o HTML está completo no primeiro fetch, então não há uma etapa de renderização separada aguardando para esse conteúdo.
- O renderizador não mantém estado. Não há cookies,
localStorageousessionStorageentre carregamentos; ele recusa solicitações de permissão. Não condicione conteúdo ao estado do cliente. - Ele não clica nem rola a página, e renderiza em uma viewport muito alta. Conteúdo atrás de uma interação não será visto.
- Os recursos são armazenados em cache agressivamente — use nomes de arquivo com fingerprint de conteúdo (o padrão do Angular,
main.<hash>.js) para que pacotes atualizados não sejam servidos como obsoletos.
@angular/ssr — o que é e a história do nome
A renderização no servidor executa o Angular em um servidor Node a cada solicitação e devolve HTML completamente renderizado; o navegador então faz hydration (anexa listeners de eventos ao DOM existente em vez de renderizar novamente). Todo rastreador — Googlebot, Bingbot e bots sociais — recebe HTML completo na primeira solicitação, sem precisar de uma segunda onda.
O nome confunde, então seja preciso: “Angular Universal” era a solução histórica de SSR, distribuída como um pacote externo (@nguniversal/express-engine). Com o Angular v17 (novembro de 2023), o SSR foi integrado diretamente à CLI do Angular e ao Application Builder e renomeado como @angular/ssr; o repositório Angular Universal agora está em modo de manutenção. Evidence for this claim Modern Angular documents @angular/ssr as its server-side and hybrid rendering package. Scope: Current Angular documentation; historical Angular Universal details are not required for this claim. Confidence: high · Verified: Angular: Server-side and hybrid rendering A configuração agora é feita com uma única linha:
# New project with SSR enabled
ng new my-app --ssr
# Add SSR to an existing project
ng add @angular/ssrIsso substitui o antigo ng add @nguniversal/express-engine. A mesma ideia, agora como ferramenta de primeira classe.
Pré-rendering (geração estática / SSG)
O pré-rendering gera HTML estático para as rotas durante o build, então nenhum servidor é executado no momento da solicitação — você pode fazer o deploy em uma CDN. Ele oferece o TTFB/FCP/LCP mais rápido e o menor custo operacional. Na v17+ você o configura por rota em um arquivo de rotas do servidor (app.routes.server.ts) usando RenderMode.Prerender, e outputMode: 'static' produz um aplicativo totalmente estático.
A restrição: os dados precisam estar disponíveis no momento do build, não pode haver conteúdo por usuário e sites muito grandes significam builds lentos. É ideal para páginas de marketing, documentação e posts de blog — exatamente o conteúdo que mais precisa ranquear e ser citado.
Renderização híbrida — escolha um modo por rota
O grande avanço da v17+ é que o modo de renderização é uma decisão por rota, configurada em app.routes.server.ts:
RenderMode.Prerender— rotas estáticas (home, sobre, posts de blog).RenderMode.Server— rotas dinâmicas, por solicitação (resultados de busca, painéis com dados recentes).RenderMode.Client— rotas somente internas que você não quer indexar (interfaces administrativas, telas autenticadas). CSR é realmente adequado aqui.
Esta é a regra prática de decisão: faça pré-rendering do que é estático, renderize no servidor o que precisa estar atualizado e só recorra à renderização no cliente para o que não deve entrar no índice.
Hydration — a armadilha que destrói o CLS
O SSR ingênuo tem um defeito: o servidor envia HTML e depois o navegador o descarta e renderiza tudo novamente, causando um flash e trabalho desperdiçado. Hydration corrige isso — o navegador restaura o aplicativo renderizado pelo servidor e reutiliza o DOM correspondente em vez de destruí-lo e recriá-lo, conectando apenas a interatividade. Ative-a em app.config.ts:
provideClientHydration()Pré-requisito: hydration é um complemento do lado do cliente para SSR, não uma chave independente. provideClientHydration() só faz algo em uma rota que já é renderizada no servidor (ou pré-renderizada) — não transforma o shell de uma rota RenderMode.Client em HTML de servidor. Ative SSR/pré-rendering primeiro para a rota.
Duas capacidades mais recentes importam para a UX relacionada ao SEO — nenhuma delas muda a indexação na Busca:
- Replay de eventos (v18+): captura interações do usuário compatíveis que acontecem antes do fim da hydration e as reproduz quando ela termina. Reduz cliques perdidos para os tipos de interação que suporta; é um recurso de UX/interatividade, não de SEO, e não altera o que o Google indexa.
- Hydration incremental (prévia para desenvolvedores na v19, estável na v20): depende de SSR, hydration, views adiáveis e replay de eventos em conjunto — não é um recurso independente. É alimentada por blocos
@defercom gatilhos de hydration que controlam quais limites permanecem desidratados na renderização inicial; um limitehydrate neverpermanece desidratado no primeiro carregamento, mas não fica necessariamente impedido de carregar suas dependências em renderizações posteriores no cliente (por exemplo, após uma mudança de rota). O efeito relevante para SEO é indireto: menos JavaScript hidratado de início pode ajudar o LCP, mas a configuração do gatilho é um detalhe de renderização, não um mecanismo de tempo do rastreador.
A armadilha crítica: colocar @if (isPlatformBrowser(...)) diretamente em um template faz o servidor e o cliente renderizarem marcação diferente — uma incompatibilidade de hydration — que produz mudança de layout e prejudica o CLS. Use afterNextRender() para trabalho exclusivo do navegador em vez de ramificar o template com base na plataforma.
Títulos e meta tags — os serviços integrados do Angular
O Angular não consegue vincular diretamente ao texto do elemento <title>, então você gerencia as tags do head por meio de dois serviços de @angular/platform-browser:
Title—setTitle()/getTitle().Meta—addTag(),addTags(),updateTag(),getTag(),removeTag(), com seletores comoname='description'ouproperty='og:title'.
Você não precisa de uma biblioteca de terceiros para o básico. Um serviço de SEO compartilhado é o padrão mais limpo:
@Injectable({ providedIn: 'root' })
export class SeoService {
private title = inject(Title);
private meta = inject(Meta);
updatePage(title: string, description: string) {
this.title.setTitle(title);
this.meta.updateTag({ name: 'description', content: description });
this.meta.updateTag({ property: 'og:title', content: title });
}
}Especificamente para títulos, a TitleStrategy do roteador (Angular v14+) permite definir um title diretamente na configuração da rota, para que o título da página seja atualizado automaticamente na navegação — sem código por componente. E não defina document.title = ... manualmente; use o serviço Title para que funcione corretamente sob SSR.
Estrutura de URL
- Use o roteamento padrão da API History do HTML5 (
PathLocationStrategy) — URLs limpas como/products/shoes. Ele precisa de<base href="/">emindex.html. - Nunca use
HashLocationStrategy/useHash: truepara conteúdo público. Identificadores de fragmento depois de#são removidos antes da solicitação HTTP, então o servidor nunca os vê e o Googlebot não consegue resolver#/productsde modo confiável — seu site inteiro pode acabar como uma única URL. - Links reais
<a href>para navegação, inclusive para rotas carregadas de forma preguiçosa. Lazy loading é adequado para performance, mas os links para essas rotas ainda precisam ser âncoras rastreáveis, não manipuladores de clique.
Dados estruturados (JSON-LD)
O Google aceita a injeção de JSON-LD com JavaScript. O padrão robusto no Angular é um serviço que cria um <script type="application/ld+json"> e o anexa a document.head, usando o token de injeção DOCUMENT do Angular em vez de tocar em document globalmente (o que quebra no servidor). Mantenha todo o schema em um só lugar — não o divida entre o HTML estático e o DOM renderizado — e valide com o Rich Results Test e o URL Inspection.
Renderização dinâmica — uma solução provisória antiga, não um plano
A renderização dinâmica entrega uma versão pré-renderizada aos bots (via Puppeteer, Rendertron ou prerender.io) e a SPA completa aos usuários. O Google é explícito: “dynamic rendering is a workaround and not a long-term solution” (tradução) «a renderização dinâmica é uma solução provisória, não uma solução de longo prazo», e há “better solutions than dynamic rendering” (tradução) «soluções melhores que a renderização dinâmica» — especificamente renderização no servidor, renderização estática ou hydration. Ela não é automaticamente cloaking, desde que você entregue conteúdo substancialmente semelhante, mas acrescenta outro servidor de renderização, cria risco de divergência de conteúdo e não faz nada pelos Core Web Vitals de usuários reais. Em um novo build Angular, escolha @angular/ssr ou pré-rendering.
Erros comuns de SEO no Angular
- Roteamento com hash (URLs com
#) — o site inteiro parece uma única URL. - Ausência de chamadas
Title/Meta— todas as páginas compartilham um título e uma descrição. - Ausência de SSR/pré-rendering — o conteúdo só existe depois da onda de renderização adiada.
- Bloqueio de
.js/.cssemrobots.txt— o Google não consegue renderizar e indexa um shell vazio. - Retorno de
200em uma página de não encontrado — um soft 404; retorne um404real ou adicionenoindex. document.title = ...em vez do serviçoTitle.isPlatformBrowser()dentro de@ifno template — incompatibilidade de hydration → CLS.- Acesso a
window/localStorage/documentem código executado no servidor — falhas de SSR. - Divisão do schema entre HTML bruto e DOM renderizado.
- Teste em desenvolvimento local em vez de usar URL Inspection — confiar em suposições, não no Googlebot.
Testando Angular para SEO
- URL Inspection (Search Console) é a fonte de verdade: a visualização da página rastreada mostra o HTML renderizado — o DOM depois que o Googlebot executou o JavaScript, que é o que é indexado — além de mensagens do console JS e recursos bloqueados. Execute o Live Test para uma renderização sob demanda.
- Use
curlna URL para ver o HTML bruto, antes do JS — um shell vazio significa CSR sem SSR. - Rich Results Test valida dados estruturados.
- Ahrefs Site Audit (com renderização JS ativada) e Screaming Frog (modo JS) comparam DOM bruto e renderizado em escala.
- Lighthouse / PageSpeed Insights medem o impacto da sua escolha de renderização nos Core Web Vitals.
Angular não é ruim para SEO — é apenas diferente. Coloque HTML real na resposta, gerencie suas meta tags, mantenha URLs e links rastreáveis e deixe as próprias ferramentas do Google arbitrarem o que foi renderizado. Este tópico fica ao lado de JavaScript SEO e das questões de renderização de headless CMS neste cluster — a lição subjacente é a mesma em todos: o modo de renderização decide quase tudo.
Resumo de IA
Uma versão condensada da versão Advanced:
- O padrão do Angular é a renderização no lado do cliente (CSR) — um shell HTML quase vazio. Isso significa indexação atrasada/não confiável, LCP pior e rastreadores que não são do Google (Bing e redes sociais) vendo nada.
- O Googlebot é evergreen e renderiza JS, mas a renderização é enfileirada (as “duas ondas” — HTML bruto primeiro, DOM renderizado depois, sem prazo fixo), sem estado (sem cookies/armazenamento), não clica nem rola e aplica cache agressivo aos recursos. SSR/pré-rendering contorna a espera — o HTML está completo no primeiro fetch, sem uma etapa de renderização separada aguardando.
@angular/ssré a correção moderna — SSR está na CLI do Angular desde a v17, substituindo o Angular Universal mantido externamente (@nguniversal/express-engine). Configuração:ng new --ssroung add @angular/ssr.- Pré-rendering (HTML estático gerado no build) é a opção mais rápida e pode ser entregue por CDN — ideal para conteúdo estático de marketing/blog/documentação; limitado aos dados disponíveis no build.
- Renderização híbrida define o modo por rota em
app.routes.server.ts:RenderMode.Prerender(estático),RenderMode.Server(dinâmico),RenderMode.Client(páginas internas não indexadas). - Hydration (
provideClientHydration()) reutiliza HTML do servidor em uma rota já SSR/pré-renderizada — não cria HTML de servidor para CSR. Replay de eventos (v18+) captura interações pré-hydration compatíveis e as reproduz depois; hydration incremental (prévia na v19 / estável na v20) depende de SSR + hydration + views adiáveis + replay de eventos, usando gatilhos de hydration@deferpara controlar limites desidratados no carregamento inicial (hydrate nevernão impede carregamentos posteriores no cliente). Nenhuma das duas muda a indexação na Busca. EviteisPlatformBrowser()em@ifde template — isso causa incompatibilidade de hydration → CLS; useafterNextRender(). - Use os serviços integrados
TitleeMeta(não é necessária biblioteca de terceiros); aTitleStrategydo roteador (v14+) define títulos por rota automaticamente. - Use roteamento HTML5 History, nunca URLs com hash (
#); a navegação deve usar âncoras reais<a href>. JSON-LD pode ser injetado pelo tokenDOCUMENT. - Renderização dinâmica é uma solução provisória, não uma estratégia — o Google recomenda SSR/renderização estática/hydration.
- Teste com URL Inspection (HTML renderizado),
curl(HTML bruto), Rich Results Test e um rastreador com renderização JS. AngularJS ≠ Angular — conselhos antigos sobre AngularJS não se aplicam.
Documentação oficial
Documentação de fonte primária do Google e do Angular.
- Entenda os fundamentos de SEO para JavaScript — as fases de rastreamento → renderização → indexação, links
<a href>rastreáveis, alertas sobre roteamento por fragmentos, soft 404s e dados estruturados injetados com JS. - Renderização dinâmica (solução provisória) — por que é uma solução provisória, a nuance de cloaking e as alternativas de SSR/renderização estática/hydration.
- Renderização na Web (web.dev — Addy Osmani e Jason Miller) — definições de referência para SSR, CSR, renderização estática e hidratação, além das compensações de desempenho.
- Ferramenta URL Inspection — como ver o HTML renderizado que o Google realmente indexa, além das mensagens do console JS.
Angular
- Angular — renderização no servidor e híbrida (SSR) — o guia oficial de
@angular/ssr, rotas do servidor eRenderMode. - Angular — serviço
Title—setTitle()/getTitle(). - Angular — serviço
Meta—addTag(),updateTag()e seletores. - Angular — referência do roteador —
PathLocationStrategyversus roteamento com hash,TitleStrategy. - Apresentando o Angular v17 (blog da equipe Angular) — SSR se tornando um recurso de primeira classe da CLI.
- Angular Universal (modo de manutenção) — o pacote histórico, agora substituído por
@angular/ssr.
Citações da fonte
Declarações registradas do Google e do meu próprio trabalho. Cada link de mecanismo de busca é um link profundo que salta para a passagem citada.
Google — como os aplicativos JavaScript são processados
- “Google processes JavaScript web apps in three main phases: 1. Crawling 2. Rendering 3. Indexing.” (tradução) «O Google processa aplicativos web JavaScript em três fases principais: 1. Rastreamento 2. Renderização 3. Indexação.» — Documentação do Google Search Central. Ir para a citação
- “Google can only discover your links if they are <a> HTML elements with an href attribute.” (tradução) «O Google só consegue descobrir seus links se eles forem elementos HTML <a> com um atributo href.» — Documentação do Google Search Central. Ir para a citação
Google — a renderização dinâmica é uma solução provisória
- “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 era uma solução provisória, não uma solução de longo prazo para problemas com conteúdo gerado por JavaScript nos mecanismos de busca.» — Documentação do Google Search Central. Ir para a citação
web.dev — prefira SSR / renderização estática (Addy Osmani e Jason Miller)
- “Rendering an app on the server to send HTML, rather than JavaScript, to the client.” (tradução) «Renderizar um aplicativo no servidor para enviar HTML, em vez de JavaScript, ao cliente.» — a definição de renderização no servidor. Ir para a citação
Patrick Stox (meu próprio trabalho — JavaScript SEO: A Definitive Guide)
- “JavaScript is not bad for SEO, and it’s not evil. It’s just different from what many SEOs are used to.” (tradução) «JavaScript não é ruim para SEO nem é maligno. É apenas diferente do que muitos profissionais de SEO estão acostumados.»
- “Any kind of SSR, static rendering, and prerendering setup is going to be fine for search engines.” (tradução) «Qualquer configuração de SSR, renderização estática ou pré-rendering funcionará bem para os mecanismos de busca.»
Checklist de SEO para Angular
Uma verificação rápida para confirmar que um aplicativo Angular pode ser rastreado e indexado:
- Conteúdo público é servido como HTML real por
@angular/ssr(SSR) ou pré-rendering, não por CSR completo. - O modo de renderização é definido por rota em
app.routes.server.ts— faça pré-rendering de rotas estáticas, renderize rotas dinâmicas no servidor e use o cliente apenas em páginas internas não indexadas. - Hydration está ativada (
provideClientHydration()) e nenhum template usaisPlatformBrowser()dentro de@if(useafterNextRender()). - Cada página define um título exclusivo (pelo serviço
Titleou pelo roteadorTitleStrategy) e uma descrição exclusiva (pelo serviçoMeta). - Tags Open Graph / Twitter Card são definidas pelo serviço
Metapara prévias sociais. - O roteamento usa a API History do HTML5 (padrão) com
<base href="/">— nãoHashLocationStrategy/useHash: true. - Toda navegação usa âncoras reais
<a href>, inclusive links para rotas carregadas de forma preguiçosa. -
robots.txtnão bloqueia recursos.jsou.css. - Views de não encontrado no cliente retornam um
404real ou carregamnoindex(sem soft 404s). - Nenhum acesso a
window/localStorage/documentocorre em código executado no servidor (use o tokenDOCUMENT/ guards de plataforma). - JSON-LD está em um só lugar e passa no Rich Results Test.
- Você verificou o HTML renderizado no URL Inspection — não apenas no desenvolvimento local.
Os modelos mentais
1. Coloque HTML real na resposta. Quase todo problema de SEO no Angular se reduz a uma pergunta: o rastreador recebe HTML concluído no primeiro fetch ou um shell que precisa renderizar? SSR e pré-rendering respondem “sim”. CSR responde “eventualmente, talvez”. Comece toda auditoria aqui.
2. A regra de decisão de renderização por rota.
- Conteúdo estático (home, sobre, blog, documentação) →
RenderMode.Prerender. - Conteúdo dinâmico que precisa estar atualizado (busca, dados ao vivo) →
RenderMode.Server. - Páginas internas/autenticadas que você não quer indexar →
RenderMode.Clienté adequado.
3. “Angular Universal” e “@angular/ssr” são a mesma ideia, em épocas diferentes.
Universal era o pacote externo; na v17 o Angular incorporou o SSR à CLI e mudou seu nome. Em um Angular moderno, você quer @angular/ssr — o repositório Universal está em modo de manutenção.
4. Faça hydration, não renderização novamente.
O SSR ingênuo envia HTML e depois o descarta. provideClientHydration() o reutiliza — mas somente em uma rota que já é SSR/pré-renderizada; não cria HTML de servidor para uma rota CSR. Replay de eventos e hydration incremental (@defer) reduzem o JavaScript inicial. Nunca ramifique um template com isPlatformBrowser() — é assim que surge uma incompatibilidade de hydration e um impacto no CLS.
5. As head tags são sua responsabilidade, não do Angular.
Não há um Yoast aqui. Defina títulos e descrições deliberadamente com os serviços Title/Meta (ou TitleStrategy), por página. “Todas as páginas compartilham um título” é a falha padrão, não azar.
6. Projete para um bot sem estado em URLs limpas.
Roteamento pela API History, âncoras reais <a href>, nenhuma dependência de cookies/armazenamento e nenhum conteúdo atrás de um clique. Depois deixe o URL Inspection — não seu laptop — dizer o que foi renderizado.
SEO para Angular — ficha rápida
Modos de renderização
| Modo | Onde o HTML é criado | SEO | Melhor para | Configuração Angular |
|---|---|---|---|---|
| Prerender (SSG) | Durante o build → arquivos estáticos | ✅ Melhor | Marketing/blog/documentação estáticos | RenderMode.Prerender |
| SSR | Servidor, por solicitação | ✅ Ótimo | Conteúdo dinâmico que precisa estar atualizado | RenderMode.Server / @angular/ssr |
| Client (CSR) | No navegador | ⚠️ Arriscado | Páginas internas/autenticadas (não indexadas) | RenderMode.Client |
| Renderização dinâmica | Servidor separado para bots | Apenas solução provisória | Aplicativos legados que não podem migrar | Puppeteer / Rendertron / prerender.io |
Comandos de configuração
| Objetivo | Comando |
|---|---|
| Novo projeto com SSR | ng new my-app --ssr |
| Adicionar SSR a um projeto existente | ng add @angular/ssr |
| Ativar hydration | provideClientHydration() em app.config.ts |
Regras rápidas para head e roteamento
- Títulos/descrições: serviços
Title+Metado Angular (não é necessária biblioteca de terceiros). - Títulos automáticos por rota:
TitleStrategydo roteador (v14+) pela propriedadetitleda rota. - Roteamento: API History do HTML5 +
<base href="/">. NuncauseHash: true. - Links: âncoras reais
<a href>— inclusive para rotas carregadas de forma preguiçosa. - JSON-LD: injete pelo token
DOCUMENT; mantenha-o em um único lugar.
Nomes
- Angular Universal = pacote externo antigo (
@nguniversal/express-engine), em modo de manutenção. @angular/ssr= o mesmo SSR, integrado à CLI desde a v17.- AngularJS (v1.x) ≠ Angular (v2+) — frameworks diferentes; conselhos antigos sobre AngularJS não se aplicam.
Armadilhas
isPlatformBrowser()em@ifde template → incompatibilidade de hydration → CLS. UseafterNextRender().window/localStorage/documentno servidor → falha de SSR.- Bloquear
.js/.cssem robots.txt → o Google não consegue renderizar.
Como verificar se um aplicativo Angular é realmente renderizado no servidor
A maneira mais rápida de saber se uma URL usa CSR ou SSR/pré-rendering é buscar o HTML bruto (antes de qualquer JavaScript) e procurar seu conteúdo real. Um aplicativo Angular em CSR retorna um <app-root> quase vazio; um aplicativo SSR/pré-renderizado retorna marcação concluída.
macOS / Linux
# Raw HTML as the server sends it — this is the "first fetch", pre-JS
curl -sL -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://example.com/page/ -o raw.html
# Is your real headline in the raw HTML? Empty result = CSR with no SSR
grep -o "Your headline text" raw.html
# A near-empty <app-root> is the tell-tale CSR signature
grep -o "<app-root></app-root>" raw.htmlWindows (PowerShell)
$ua = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
Invoke-WebRequest -Uri "https://example.com/page/" -UserAgent $ua -OutFile raw.html
Select-String -Path raw.html -Pattern "Your headline text"
Select-String -Path raw.html -Pattern "<app-root></app-root>"Se o título estiver ausente e você vir um <app-root> vazio, o conteúdo depende de renderização — adicione SSR ou pré-rendering. (Para o DOM renderizado, use “View Crawled Page → rendered HTML” do URL Inspection; um curl comum não executa JS.)
Confirme que você não está bloqueando o JS/CSS do Angular em robots.txt
macOS / Linux
curl -sL https://example.com/robots.txt | grep -iE "disallow.*\.(js|css)|Disallow:\s*/(assets|dist|main)"Um Disallow que corresponda ao seu pacote significa que o Google não consegue renderizar a página corretamente — quase sempre um erro.
Ferramentas para depurar SEO no Angular
- URL Inspection (Google Search Console) — a fonte de verdade. Execute um Live Test e leia o HTML renderizado (o DOM depois que o Googlebot executou seu JavaScript), a captura de tela, os recursos da página (o que carregou versus o que foi bloqueado) e as mensagens do console JavaScript.
- Rich Results Test — confirme que seu JSON-LD está presente na saída renderizada após qualquer mudança de renderização.
curl— busque o HTML bruto, antes do JS, para distinguir CSR (<app-root>vazio) de SSR/pré-rendering.- Ahrefs Site Audit (com renderização JS ativada) — rastreia com Chrome headless e compara o DOM bruto ao renderizado em escala, destacando meta tags ausentes, canonicals quebradas e problemas de indexabilidade.
- Screaming Frog SEO Spider (modo de renderização JS) — compare o DOM bruto e renderizado por URL.
- Lighthouse / PageSpeed Insights — meça o impacto da sua estratégia de renderização nos Core Web Vitals.
- Angular CLI / DevTools — confirme o
outputMode, as rotas do servidor e a configuração de hydration.
Recursos que valem seu tempo
Meus textos relacionados
- JavaScript SEO: A Definitive Guide — meu guia completo sobre renderização, paridade do DOM, a regra da diretiva mais restritiva e quais configurações de renderização são seguras. SEO para Angular é uma aplicação específica de tudo isso.
- The Beginner’s Guide to Technical SEO — onde renderização e rastreamento se encaixam no quadro maior.
Minhas palestras
- JavaScript SEO — Ungagged 2019 (SlideShare) — o comportamento de renderização sem estado do Googlebot, viewport e cache, além das abordagens de renderização da época. (Aviso permanente: a recomendação de renderização dinâmica nessa apresentação está desatualizada — o Google passou a chamá-la de solução provisória.)
De outros pontos do setor
- Renderização na Web (web.dev) — o texto de referência de Addy Osmani e Jason Miller sobre SSR, CSR, renderização estática e compensações da hidratação.
- Renderização no servidor e híbrida (SSR) (angular.dev) — o guia oficial de
@angular/ssr: rotas do servidor,RenderMode, pré-renderização e hidratação. - Apresentando o Angular v17 (blog da equipe Angular) — o lançamento que tornou o SSR um recurso de primeira classe da CLI e introduziu o pacote
@angular/ssr. - Angular Universal (modo de manutenção) (GitHub) — o pacote histórico de SSR que
@angular/ssrsubstituiu, útil para entender a mudança de nome. - Guia de SEO para Angular (Search Engine Journal, Jamie Indigo) — o guia clássico da indexação em duas ondas; forte nos fundamentos, embora seja anterior à mudança de nome da v17.
- Guia de SSR para Angular (Angular Architects, Alexander Thalhammer, março de 2025) — um guia de configuração de SSR com muito código; confira as amostras de código na documentação oficial de
@angular/ssrpara verificar mudanças de API desde a publicação. - r/TechSEO — a comunidade para depuração de renderização e indexação.
Antipadrões de SEO no Angular
Erros concretos que vejo repetidamente em aplicativos Angular — cada um é um hábito que vale verificar diretamente, não apenas um risco teórico.
Enviar um build apenas com CSR e considerá-lo pronto
A saída padrão de ng new não tem SSR nem pré-rendering. É a maneira mais rápida de começar um projeto e a mais fácil de terminar com um <app-root> vazio no primeiro fetch. Por que está errado: o HTML bruto que um rastreador vê não tem conteúdo, então a indexação depende inteiramente da etapa de renderização adiada do Google — e outros bots não têm uma segunda chance. Faça isto: adicione @angular/ssr no início do projeto (ng new my-app --ssr) ou execute ng add @angular/ssr em um projeto existente antes de publicar qualquer coisa que queira tornar encontrável.
Usar HashLocationStrategy em rotas públicas
O roteamento com hash (useHash: true, URLs como /#/products/shoes) ainda é padrão em alguns tutoriais e templates antigos do Angular. Por que está errado: tudo depois do # é removido no cliente antes que a solicitação chegue ao servidor, então o servidor — e o Googlebot — só veem uma URL para o aplicativo inteiro. Faça isto: use o roteamento padrão da API History do HTML5 (PathLocationStrategy) com <base href="/"> em index.html.
Ramificar um template com base em isPlatformBrowser()
Envolver conteúdo em @if (isPlatformBrowser(platformId)) parece a maneira óbvia de proteger código exclusivo do navegador. Por que está errado: o servidor renderiza uma ramificação e o cliente renderiza outra durante a hydration, gerando uma incompatibilidade de hydration — o Angular precisa reconciliar a diferença e o resultado visível é uma mudança de layout que aparece como CLS. Faça isto: use afterNextRender() para trabalho exclusivo do navegador, para que o template seja renderizado de forma idêntica no servidor e no cliente.
Definir document.title diretamente em vez de usar o serviço Title
Funciona no desenvolvimento local, então é um atalho fácil de escolher. Por que está errado: o acesso direto ao DOM, como document.title = '...', não funciona bem com SSR — o servidor não tem um global document no mesmo sentido que o navegador, e você perde os benefícios da integração do roteador do próprio Angular. Faça isto: injete o serviço Title do Angular (setTitle()) ou configure títulos por rota com a TitleStrategy do roteador.
Tocar em window, localStorage ou document no código executado durante SSR
Um componente ou serviço que lê localStorage ou verifica window.innerWidth no momento da construção funciona bem no navegador e faz a renderização do servidor falhar. Por que está errado: nenhum desses globals existe no processo de servidor Node que executa seu build SSR, então a renderização lança um erro e a solicitação retorna 500s ou silenciosamente recua para uma resposta vazia. Faça isto: proteja esse código com afterNextRender() ou injete o token DOCUMENT do Angular em vez do global, e teste o build SSR localmente (ng build + servir a saída SSR), não apenas com ng serve.
Tratar renderização dinâmica como uma correção permanente
Montar Puppeteer ou um serviço como Rendertron para servir uma captura pré-renderizada aos bots resolve o sintoma imediato. Por que está errado: é um sistema extra para manter, pode divergir do que usuários reais veem e o Google afirmou diretamente que é uma solução provisória, não uma solução de longo prazo. Faça isto: migre para @angular/ssr ou pré-rendering para que cada solicitante — bot ou pessoa — receba o mesmo HTML real do mesmo pipeline.
Qual modo de renderização esta rota deve usar?
O Angular v17+ permite definir o modo de renderização por rota em app.routes.server.ts. A pergunta não é “SSR ou pré-rendering” para o aplicativo inteiro — é esta, feita para cada rota.
Choosing a rendering mode for an Angular route
Prompts para trabalho de SEO no Angular
Prompts prontos para copiar para as tarefas específicas de SEO para Angular deste artigo. Cole a entrada descrita e confira a saída com seu próprio julgamento — eles economizam tempo nas partes mecânicas, mas não substituem os testes com URL Inspection.
1. Comparar HTML bruto e renderizado de uma rota
Cole a saída de curl -sL <url> (HTML bruto) e o painel “rendered HTML” de um Live Test do URL Inspection (ou de um rastreamento com renderização JS do Ahrefs/Screaming Frog) para a mesma URL.
Here is the raw HTML for [URL] (fetched with curl, before JavaScript runs):
[paste raw HTML]
Here is the rendered HTML for the same URL (from Google Search Console URL
Inspection's Live Test, or a JS-rendering crawler):
[paste rendered HTML]
Compare the two. List: (1) content present in rendered but missing from raw —
this is what depends on client-side rendering, (2) any <title>, meta description,
or JSON-LD that differs between the two versions, (3) whether the raw HTML shows
a near-empty <app-root> (a sign of CSR with no SSR/prerendering).Espere receber uma lista curta do que depende de CSR e de qualquer divergência de título/meta/schema entre as versões bruta e renderizada — as duas coisas que vale corrigir primeiro.
**2. Revisar uma implementação de serviço Title/Meta
Cole seu SeoService do Angular (ou equivalente) que chama os serviços Title e Meta.
Here is an Angular service that sets page titles and meta tags:
[paste the service's TypeScript code]
Check it against these rules: (1) titles are set via the Title service's
setTitle(), never document.title directly, (2) description is set with
meta.updateTag({ name: 'description', ... }) not addTag() (which can duplicate
the tag on repeat calls), (3) Open Graph tags use the property selector, not
name, (4) nothing in this code reads window/localStorage/document directly in a
way that would break during SSR. Flag any line that violates one of these and
suggest the fix.Espere uma avaliação linha a linha de aprovação/reprovação contra essas quatro regras, com um trecho corrigido para qualquer item sinalizado.
**3. Auditar app.routes.server.ts em busca de erros no modo de renderização
Cole sua configuração de rotas do servidor.
Here is my Angular app.routes.server.ts, which sets RenderMode per route:
[paste the file]
For each route, tell me: is RenderMode.Prerender used on anything that depends
on per-request or per-user data (a mistake — it would bake stale/wrong data into
the static build)? Is RenderMode.Client used on anything that looks like public,
indexable content (a missed-SEO-opportunity)? Is RenderMode.Server used on fully
static content where Prerender would be faster and cheaper? List each route with
its current mode and whether it matches the decision rule: static data →
Prerender, must-be-fresh → Server, non-indexed internal → Client.Espere um veredito por rota que sinalize qualquer rota cujo RenderMode não corresponda ao que ela realmente precisa.
Teste seus conhecimentos: SEO para Angular
Cinco perguntas rápidas sobre como tornar aplicativos Angular rastreáveis e indexáveis. Escolha uma resposta para cada pergunta e depois confira.
Registro de alterações
Atualizado em 11 de ago. 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.
Não é possível fazer a comparação completa — nenhum instantâneo anterior foi arquivado para esta revisão.
Atualizado em 8 de ago. 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.
Não é possível fazer a comparação completa — nenhum instantâneo anterior foi arquivado para esta revisão.
Atualizado em 17 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.
Não é possível fazer a comparação completa — nenhum instantâneo anterior foi arquivado para esta revisão.