<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

Por que meu blog Angular não é indexado pelo Google (e como corrigi isso)

Introdução: quando o Google simplesmente ignora seu blog

Há algumas semanas tomei conhecimento de um problema chato: os artigos publicados neste mesmo blog não apareciam no Google. Eles não estavam mal posicionados, não recebiam pouco tráfego – simplesmente não estavam indexados. Abrindo o Google Search Console, a situação ficou clara: das 32 URLs enviadas no sitemap, apenas 4 foram indexadas (home, listagem de blogs, projetos, contatos). Zero artigos.

O que se segue é o caminho real que segui para diagnosticar e resolver o problema, desde a primeira suspeita técnica até a causa oculta que ninguém verifica: conteúdo duplicado. Se você tem um blog técnico, especialmente sobre um aplicativo Angular com renderização no servidor, provavelmente reconhecerá pelo menos um desses problemas.

Primeira suspeita: páginas renderizadas no lado do cliente em vez do lado do servidor

A primeira verificação nesses casos é sempre a mesma: o que o Googlebot realmente vê quando visita uma página? Em um aplicativo Angular com SSR, se a renderização do lado do servidor não funcionar conforme o esperado, o rastreador receberá apenas o shell HTML vazio - nenhum específico, nenhuma meta descrição, nenhum conteúdo textual real, tudo preenchido via JavaScript após Angular bootstraps.</p> <p>No meu caso, a causa estava em angular.json: o projeto ainda estava usando o sinalizador booleano herdado "prerender": true em vez do mais recente "outputMode": "server". Com o sinalizador booleano, o Angular pré-renderiza apenas rotas estáticas e ignora silenciosamente getPrerenderParams() — a função que expande rotas dinâmicas como /blog/:slug buscando todos os slugs publicados pelo backend. Result: all static pages (home, projects, contacts) were pre-rendered correctly, but every single blog post remained a pure client-side page.</p> <pre><code>// angular.json — prima (sbagliato) "prerender": true // angular.json — dopo (corretto) "outputMode": "server"</code></pre> <p>Com outputMode: "server", Angular na verdade chama getPrerenderParams() para cada rota dinâmica configurada em app.routes.server.ts, que no meu caso consulta GET /blog/posts no backend e gera uma página estática para cada slug postado. Após a correção, a compilação finalmente produziu cada artigo como HTML real, com título, meta descrição e artigo JSON-LD prontos no primeiro byte.</p> <h2>Bug oculto do Apache: redirecionamentos 301 em todas as páginas pré-renderizadas</h2> <p>Tendo corrigido a pré-renderização, uma verificação com um simples curl -I (não um navegador, que segue redirecionamentos de forma transparente e esconde o problema) revelou uma segunda falha: cada página pré-renderizada respondeu com 301 Movido Permanentemente para o mesmo URL com a barra final adicionada.</p> <p>A causa é o comportamento padrão do Apache, mod_dir: quando uma solicitação corresponde a um diretório real no disco (e é exatamente isso que a pré-renderização cria: /blog/article-name/index.html), o Apache redireciona automaticamente adicionando a barra final, antes que as regras mod_rewrite no .htaccess possam intervir. O conteúdo por trás do redirecionamento estava correto, mas o URL canônico declarado em cada página (sem barra final, consistente com o mapa do site) nunca retornou 200 direto - os rastreadores sempre receberam um URL diferente daquele marcado como canônico.</p> <pre><code><IfModule mod_dir.c> DirectorySlash Off </IfModule> RewriteCond %{REQUEST_FILENAME} -d RewriteCond %{REQUEST_FILENAME}/index.html -f RewriteRule ^(.*)$ $1/index.html [L]</code></pre> <p>Ao desativar a redução automática e adicionar uma regra explícita que veicula index.html diretamente no URL sem barras, cada página pré-renderizada começou a responder 200 no URL canônico exato.</p> <h2>Sitemap estático vs sitemap gerado a partir de conteúdo real</h2> <p>Um terceiro problema, mais banal, mas igualmente bloqueador: o mapa do site continha apenas a listagem/página do blog, não os artigos individuais. Cada nova postagem publicada permanecia invisível para o Google até ser descoberta por meio de links internos – um processo lento e de forma alguma garantido.</p> <p>A solução foi transformar o script de geração do mapa do site de uma lista estática de rotas em uma função que consulta o backend para cada postagem publicada:</p> <pre><code>async function fetchBlogRoutes() { const res = await fetch(`${API_BASE_URL}/blog/posts?page=1&limit=50`); const { data, meta } = await res.json(); const posts = [...data]; for (let page = 2; page <= meta.totalPages; page++) { const r = await fetch(`${API_BASE_URL}/blog/posts?page=${page}&limit=50`); const j = await r.json(); posts.push(...j.data); } return posts.map(p => ({ loc: `/blog/${p.slug}`, lastmod: p.updatedAt })); }</code></pre> <p>O mesmo script agora também é executado como GitHub Action, todas as noites e a cada push, de modo que cada novo artigo publicado entra automaticamente no mapa do site sem intervenção manual.</p> <h2>A causa mais insidiosa: conteúdo duplicado</h2> <p>Depois de todas essas correções técnicas, a situação melhorou, mas não foi resolvida: a maioria dos artigos permaneceu no estado “Detectado, atualmente não indexado” — o Google conhecia os URLs do mapa do site, mas ainda não os considerava relevantes o suficiente para serem indexados. Analisando a lista artigo por artigo, encontrei dois posts publicados com intervalo de 28 segundos um do outro, exatamente sobre o mesmo assunto, com títulos quase indistinguíveis e conteúdo quase sobreposto — provavelmente uma postagem dupla acidental do painel do editor.</p> <p>O conteúdo duplicado não bloqueia a indexação de uma única página: é um sinal de qualidade avaliada ao nível de todo o domínio. Em um site novo, com muito pouca autoridade externa, algumas postagens duplicadas podem ser suficientes para deixar o Google mais cauteloso até mesmo com conteúdo totalmente original no mesmo domínio. Excluí a versão com menos visualizações e regenerei o mapa do site: é provavelmente a única intervenção com maior impacto na indexação geral do site.</p> <h2>Como realmente ler o relatório "Indexação de páginas" do Search Console</h2> <p>O relatório do Google Search Console agrupa páginas não indexadas por motivo, e cada rótulo requer uma ação diferente:</p> <ul> <li>Detectado, atualmente não indexado: o Google conhece o URL (geralmente do mapa do site), mas ainda não o rastreou. É uma questão de tempo e orçamento, não um erro a ser corrigido.</li> <li>Rastreada, atualmente não indexada: o Google visitou a página, mas optou por não indexá-la, muitas vezes devido ao conteúdo considerado de baixo valor ou duplicado.</li> <li>Página alternativa com tag canônica apropriada: normal para versões traduzidas do mesmo conteúdo que apontam corretamente para a versão principal — não é um erro.</li> <li>Erro de redirecionamento: preste atenção na data da última verificação. Se o bug foi corrigido após essa data, o relatório simplesmente mostra dados desatualizados: basta verificar com curl -I se o URL responde 200 hoje e usar "Validar correção" para forçar uma nova verificação.</li> </ul> <h2>Lista de verificação prática</h2> <ul> <li>Verifique com curl -I (não com o navegador) se cada página responde diretamente ao URL canônico, sem redirecionamentos ocultos.</li> <li>Verifique se o conteúdo recebido por um rastreador é idêntico ao de um navegador real: curl -A "Googlebot" deve retornar HTML completo, não um shell vazio aguardando JavaScript.</li> <li>Gere o mapa do site dinamicamente a partir de conteúdo publicado, não de uma lista estática de rotas.</li> <li>Procure títulos ou tópicos quase idênticos em suas postagens – conteúdo duplicado é um problema de domínio, não de uma única página.</li> <li>Use "Solicitar indexação" no Search Console em postagens mais recentes, em vez de esperar pelo rastreamento natural, que pode levar semanas em um domínio jovem.</li> </ul> <h2>Cronogramas realistas</h2> <p>Num domínio novo, com poucos sinais de autoridade externa, é normal que a primeira indexação demore de alguns dias (com solicitação manual) a várias semanas (via rastreamento natural). O que realmente importa é eliminar obstáculos técnicos e de qualidade de conteúdo: uma vez removidos, é apenas uma questão de dar tempo ao Google para revisar e confiar no domínio.</p> </article>

💬 Notas dos leitores

0 notas

Escreva uma nota

Partilhe a sua opinião, uma sugestão ou um elogio

Notas recentes

Ainda não há notas. Seja o primeiro a comentar!