SEO técnico para sites estáticos em dois idiomas
SEO técnico para sites estáticos em dois idiomas: rastreio, alt de imagens, hreflang, sitemap, noindex, dados estruturados e verificação no Search Console.
intermediárioPublicada em · 35 min de leitura
SEO técnico para sites estáticos em dois idiomas
Uma página bem escrita pode sumir da busca por motivos que não têm nada a ver com o texto. Um de imagem que diz “3 01 chamada”, um que aponta para a home de outro idioma, um que impede o Google de ler a regra que tiraria a página do índice. Esses erros são invisíveis para quem lê o blog e aparecem só na auditoria, e a maioria nasce na hora de criar o conteúdo, não depois.
Esta aula usa como fio condutor um caso real. O blog é um site Astro com posts em português, uma versão em inglês em /en/, hospedagem em S3 com CloudFront e domínio próprio. O Google apontou dois problemas (diagramas com alt fraco e hreflang mal estruturado), a investigação achou outros, e as correções seguiram um plano em fases. Ao terminar, você deve entender como o Google descobre, rastreia, indexa e entrega uma página, por que cada imagem precisa de um texto alternativo escrito de propósito, como declarar duas línguas sem confundir o Google, quando usar e quando nunca usar robots.txt para isso, e como conferir tudo antes e depois do deploy.
A aula pressupõe que você sabe o que é HTML e o que é uma URL. O resto aparece aqui. O que muda com o tempo (regras do Google, ferramentas) foi consultado na documentação oficial em 30 de outubro de 2026, e o que não consegui confirmar está declarado na seção de fontes. Os diagramas são animados e foram gerados com o ArchFlow.
Mapa da aula
| Parte | Seções | Pergunta que responde |
|---|---|---|
| 1. Como o Google encontra a página | 1 | Quais são as etapas entre uma URL existir e aparecer na busca? |
| 2. O que existe dentro da página | 2 e 3 | Que textos o Google lê, e como o alt dos diagramas é montado? |
| 3. Mais de um idioma | 4 | Como PT e EN se declaram sem confundir o Google? |
| 4. O que entra e o que sai do índice | 5 e 6 | Quando usar sitemap, robots.txt, noindex e ? |
| 5. Dados e domínio | 7 e 8 | Que dados estruturados e que domínio a página declara? |
| 6. Validar e evitar armadilhas | 9 e 10 | Que ferramenta enxerga o quê, e onde a verificação falha? |
Os diagramas seguem um mapa de cores fixo. Laranja é o que você controla (conteúdo, configuração, arquivos do site). Azul é o que o build ou o Google processa. Verde é o que chega ao índice ou o resultado bom. Rosa é o que dá errado. Cinza é neutro.
Parte 1: como o Google encontra a página
1. Descobrir, rastrear, indexar e entregar
Pense numa biblioteca gigantesca com um bibliotecário que nunca dorme. Antes de qualquer livro estar na estante, ele precisa saber que o livro existe, ir até a prateleira, ler o livro e decidir se ele entra no catálogo. Só então ele pode ser recomendado a quem pergunta. O Google trabalha em quatro etapas parecidas, e confundir uma com a outra é a origem de quase todo problema de SEO.
A primeira etapa é descobrir a URL. A documentação do Google lista dois caminhos: um link de uma página que ele já conhece, como uma lista de posts que aponta para um post novo, ou um sitemap que você envia com a lista de páginas. A segunda é rastrear: o Googlebot, o programa de coleta, baixa o HTML e os recursos da página. A terceira é indexar, quando o Google analisa o conteúdo e as tags principais e guarda tudo num banco de dados enorme, que chama de índice. A quarta é entregar o resultado: quando alguém pesquisa, o sistema escolhe páginas do índice e as ordena por relevância. Essa ordenação é o .
A diferença entre “rastreado” e “indexado” é a que mais confunde. O Google pode baixar uma página e decidir não guardá-la, porque ela tem noindex, porque é cópia de outra ou porque é fina demais. A própria documentação diz que a não é garantida e que nem toda página processada entra no índice. O Search Console mostra as duas situações como estados diferentes, e por isso “o Google visitou” sozinho não diz que a página vai aparecer.
A figura mostra o caminho inteiro. Em cima, dois caminhos de descoberta, o e um link de outra página, chegam ao rastreamento (azul, pulsando, porque é o passo que consome o acesso do Google). Depois vem a decisão de bloqueio no robots.txt. Se ela bloqueia, o Google nunca lê a página nem o noindex, e o caminho rosa de erro mostra isso. Se não bloqueia, a indexação lê o que está no HTML e chega a um losango: tem noindex? Se sim, a página fica fora do índice; se não, segue para a entrega, em verde. Repare que a ordem importa: o robots.txt é consultado antes do rastreio, e o noindex só é lido depois. A seção 5 explora essa ordem.
Parte 2: o que existe dentro da página
2. Título, description, H1 e hierarquia de títulos
O título da página é o que aparece como nome do resultado na busca e na aba do navegador. A documentação de títulos pede que cada página tenha um <title> próprio, descritivo e conciso, no idioma do conteúdo, e que ele não repita texto padrão de template. Isso importa porque a regra “um título por página” afeta a busca: se todas as páginas de tag do blog levam o mesmo título, o Google tem várias candidatas idênticas e escolhe qualquer uma.
A meta description é o texto de apoio do resultado. Ela pode ser usada no trecho que aparece abaixo do título, mas a documentação do Google diz que ele usa principalmente o conteúdo da página para montar o trecho, então a description pode ser substituída. Ela também não tem limite de tamanho declarado, embora o trecho seja cortado conforme a largura da tela. O validador do blog usa 120 a 160 caracteres como faixa ideal. Isso é uma convenção do projeto, não uma regra do Google, e a diferença importa quando alguém pergunta de onde vem o número.
O H1 é o título principal do conteúdo, e cada página deveria ter um só. No blog, o cabeçalho do post já gera um <h1> com o título. Só que muitos posts vieram de notas do Obsidian, em que o título da nota é o H1 e as partes começam com # Parte 1. O resultado eram duas ou mais cabeças principais numa página. A auditoria do OpenSEO apontou 88 páginas com esse problema.
A correção foi um plugin que roda no build e, quando o corpo do texto tem algum <h1>, rebaixa todos os títulos um nível. O cabeçalho continua sendo o único H1, e as partes viram H2. Os identificadores das âncoras não mudam, para o sumário e os links internos continuarem funcionando.
Na figura, a coluna da esquerda mostra o problema: dois títulos marcados como H1 (rosa) disputando o assunto da página. A coluna da direita mostra o resultado depois do plugin: o cabeçalho continua verde e único, e os títulos do corpo, em azul, descem um nível. A hierarquia fica sem pular degraus, o que também ajuda leitores de tela a navegar pelo sumário.
A mesma auditoria achou títulos e descrições repetidos. As listagens de tag compartilhavam a meta description padrão do site (170 páginas), e 46 páginas tinham título duplicado, principalmente as páginas 2, 3 e seguintes de cada tag. A correção foi gerar título e descrição próprios para cada listagem, com a contagem de posts e o número da página, e colocar as páginas finas fora do índice (seção 6).
3. Texto alternativo de imagens e diagramas
Esta é a seção que mais pesa no caso, porque foi ela que o Google apontou. O texto alternativo, o alt de uma imagem, tem duas funções. Quando a imagem não carrega, ele aparece no lugar dela. Para quem usa leitor de tela, ele é a única forma de ouvir o que a figura mostra. E o Google o usa para entender a imagem, junto com a legenda e o texto ao redor. A documentação de imagens pede um alt que descreva a imagem no contexto da página, sem palavras-chave empilhadas, e diz que o nome do arquivo dá “pistas muito leves” sobre o assunto.
O problema do blog começou com a forma como os diagramas entravam nos posts. O autor escrevia , e o texto entre colchetes virava o alt. Ele repetia o nome do arquivo, e o resultado era algo como alt="3 01 chamada". Dos 687 alts do blog, 116 seguiam esse padrão, e o menor tinha 3 caracteres. Para quem não vê a figura, aquilo não descreve nada.
Enquanto investigava, o próprio projeto revelou onde estava a descrição. Cada SVG gerado pelo ArchFlow tem um <title> e um <desc> dentro do arquivo. O título é a frase que nomeia a figura, e a descrição lista os elementos e as conexões. Um SVG real do blog traz, no começo do arquivo, algo como <title id="svg-creator-title">Amazon S3: Ciclo de Vida dos Objetos (Lifecycle Rules)</title> seguido de uma <desc> que enumera as setas. Todos os 682 SVGs do blog têm os dois elementos.
Aí está o ponto central, e é preciso separar o que é fato do que é raciocínio. Quando uma imagem é carregada por <img src="figura.svg">, o navegador trata o arquivo como imagem, e o conteúdo interno dele não faz parte do DOM da página. Um leitor de tela, por exemplo, só anuncia o alt do <img>. A documentação do Google sobre imagens fala de <title> apenas para SVG inline, e não diz se ele lê o texto de dentro de um SVG usado em <img>. O projeto concluiu que o <title> e o <desc> não chegam ao Google nesse formato, e por isso não contam como texto indexável. Essa conclusão vem do funcionamento de <img> e da diagnose do próprio blog, e não de uma regra publicada pelo Google. Por precaução, o alt precisa ser escrito de forma que funcione sozinho.
O blog faz isso no build, sem exigir que o autor reescreva cada alt. O plugin rehype-diagrams lê os primeiros 16 KB de cada SVG referenciado, extrai o título e a descrição, e decide o alt. Se o alt escrito no Markdown estiver vazio ou for igual ao nome do arquivo (a comparação ignora maiúsculas, espaços repetidos e os hífens do nome, e tira a numeração inicial), o plugin monta um alt novo com a função buildAlt. Ela junta o título com a parte útil da descrição, tirando o prefixo “Diagrama com N elementos” e a frase final de contagem, e corta o texto num limite de 160 caracteres. O corte nunca cai no meio de uma conexão: se a próxima conexão não cabe, o alt termina ali com reticências.
Rodei essa função sobre o SVG do S3 para conferir o resultado. O título é “Amazon S3: Ciclo de Vida dos Objetos (Lifecycle Rules)” e o alt gerado tem 108 caracteres: o título, seguido de “Novo Objeto / Upload → S3 Standard (Upload inicial)” e reticências. A próxima conexão, “S3 Standard → S3 Standard-IA”, não coube no limite e ficou de fora. A legenda da figura usa o título, de modo que o leitor vê “figura 4 · Amazon S3: Ciclo de Vida dos Objetos” e o Google recebe um alt com sentido.
O limite de 160 vem do código, na constante MAX_ALT do plugin, e foi fixado na decisão D3 do plano de correção. Não encontrei no repositório uma medição que mostre que 160 é o número ideal. O que a aula pode afirmar é o que o código faz: cortar em 160, sempre numa fronteira de conexão. O piso de 80 caracteres que uso como regra de escrita para os alts dos diagramas não é verificado pelo código. O validador recusa um alt com menos de 15 caracteres e um alt igual ao nome do arquivo com menos de quatro palavras, e só isso. Um alt de 40 caracteres passa, mesmo que seja pobre. Quem escreve o alt à mão precisa cumprir a regra de 80 a 160 por conta própria.
O fluxo da figura resume a decisão. O SVG e o Markdown entram pela esquerda. O plugin, em azul, lê o título e a descrição do SVG e, se o alt do Markdown for ruim, troca por um alt montado. A montagem produz um texto de até 160 caracteres, que vai para o <img> e para a legenda. Só desse ponto em diante o Google enxerga algo, e o caminho verde mostra que é o alt que chega. O desenho não traz um caminho direto do SVG ao Google, justamente porque o projeto concluiu que esse caminho não entrega o texto. Na prática, o texto descritivo precisa passar pelo alt.
Uma última regra vem da documentação: alt não serve para encaixar palavras-chave. Um alt que repete termos para ranquear faz mal ao leitor e pode ser tratado como spam. Por isso o validador do blog recusa alt vazio e alt que é só o nome do arquivo, e não exige que o alt contenha certas palavras.
Parte 3: mais de um idioma
4. hreflang, e auto-referência
Imagine uma placa de sinalização numa estrada com duas placas apontando para a mesma cidade, uma em português e outra em inglês. Cada placa diz “esta é a versão desta página nesta língua” e aponta para a outra. O hreflang é essa placa para o Google. Ele é um <link rel="alternate" hreflang="..."> que informa qual versão de idioma e de região existe para uma página, para que o Google mostre a certa para cada pessoa.
A documentação de versões localizadas impõe três regras que quebram a implementação quando ignoradas. A primeira é o retorno recíproco: se a página em português aponta para a versão em inglês, a versão em inglês precisa apontar de volta. Sem isso, o Google ignora as tags. A segunda é a auto-referência: cada versão lista a si mesma junto com as outras. A terceira é usar URLs absolutas, com o protocolo. O Google aceita os três métodos de declaração, que são o <head> da página, o cabeçalho HTTP e o sitemap, e diz que eles são equivalentes.
O valor x-default cumpre outro papel. Ele indica a página para quando nenhum idioma combina com o do navegador do visitante. O blog usa a versão em português como x-default, porque o português é o idioma principal do site.
No blog, o Base.astro emite essas tags no <head> de cada página que tem tradução. No post de arquitetura hexagonal, o HTML declara três links: pt-BR apontando para /posts/arquitetura-hexagonal/, en apontando para /en/posts/hexagonal-architecture/ e x-default apontando para a versão em português. Repare que os slugs são diferentes nos dois idiomas. Esse detalhe causou o primeiro problema do sitemap.
O sitemap precisa dizer a mesma coisa. O @astrojs/sitemap só pareia URLs quando o caminho é idêntico nos dois idiomas, e como os posts traduzidos têm slugs diferentes, o sitemap saiu sem as anotações de idioma para a maioria deles. Apenas 34 das 354 URLs tinham xhtml:link. O HTML declarava o par e o sitemap não, o que é exatamente a divergência que o Google cita como sinal fraco. A correção, no arquivo src/lib/sitemap-links.mjs, pareia os posts pelo translationKey do frontmatter, a mesma chave que o HTML usa. Depois do build, o sitemap traz, para cada post traduzido, os dois idiomas e o x-default, e cada URL aparece com auto-referência, como a documentação exige. O teste automático do validador compara o conjunto de links do HTML com o do sitemap e falha se os dois divergirem.
A figura mostra o pareamento. As duas páginas, em laranja, declaram uma à outra no próprio <head> (azul), com retorno recíproco na seta de ida e volta. O sitemap também as liga (azul, na parte de baixo), e o Google compara os dois. O caso da direita é o problema que o blog tinha no seletor de idioma: um post em português sem tradução levava o botão EN para a home de /en/, com hreflang="en". A home não é a versão daquele artigo, então o atributo mentia. Hoje o botão continua visível, mas sem o atributo, com o aviso de que não existe versão em inglês, e a linha pontilhada mostra essa ligação sem declaração.
A regra que sai daqui é simples. O hreflang só afirma uma equivalência quando ela existe nos dois sentidos. Antes de declarar um par, confirme que a outra página existe, é a versão do mesmo conteúdo e também declara a primeira.
Parte 4: o que entra e o que sai do índice
5. Sitemap, robots.txt e noindex
Essas três ferramentas são confundidas com frequência, porque todas dizem respeito ao que o Google faz com as páginas. Elas respondem a perguntas diferentes.
O sitemap é uma lista de URLs que você quer que o Google conheça. Ele ajuda na descoberta e não garante indexação. O blog gera um sitemap-index.xml, que aponta para o sitemap-0.xml com as páginas. A documentação permite que o sitemap seja enviado pelo Search Console e também indicado no robots.txt, e limita cada arquivo a 50 mil URLs ou 50 MB.
O robots.txt é um arquivo de instruções para o rastreamento, na raiz do domínio. No blog, ele é gerado por src/pages/robots.txt.ts a partir do domínio configurado, e o conteúdo é curto: libera tudo com Allow: / e aponta o Sitemap:. Ele não tem nenhum Disallow, e isso é uma decisão, não um esquecimento, como a seção seguinte explica.
O noindex é uma diretiva que fica dentro da página, na meta tag <meta name="robots" content="noindex">, e diz ao Google para não mostrar a página na busca. No blog, o layout base emite essa meta quando a página pede. Páginas que não devem aparecer no Google recebem a flag: o fallback offline, a 404, as páginas pessoais de destaques e lista de leitura, as páginas 2 em diante das listagens e as tags com poucos posts (seção 6). Além da meta, o build remove essas URLs do sitemap. O arquivo sitemap-noindex.mjs lê quais páginas têm noindex e tira do sitemap-0.xml tanto as URLs quanto os links de idioma que apontam para elas. A regra vive numa fonte só, a própria página, então o sitemap nunca diverge dela.
A armadilha clássica é bloquear no robots.txt uma página que você quer tirar do índice. A lógica parece boa: se o Google não pode entrar, ele não indexa. Só que, para ler o noindex, o Google precisa rastrear a página. A documentação é direta: se o robots.txt bloqueia o rastreamento, as informações sobre regras de indexação não são encontradas e são ignoradas. O resultado é uma página que continua sendo listada, às vezes só com o endereço, e uma diretiva que ninguém leu. É como colocar uma placa de “não entre” numa porta e trancá-la: quem precisava ler a placa nunca chega até ela.
Por isso o plano do blog tratou o /offline/ assim: recebeu noindex e saiu do sitemap, sem Disallow. O comentário em robots.ts registra o motivo, e a função buildRobots não menciona NOINDEX_PATHS.
A figura ensina a decisão em ordem. O primeiro losango pergunta se a intenção é tirar a página do Google. Se não, a página continua indexada e nada precisa ser feito, como mostra o caminho verde à direita. Se sim, a pergunta seguinte é se o robots.txt bloqueia a página. Sim leva ao caminho rosa de erro silencioso, porque a regra nunca será lida. Não leva ao último losango, que procura a meta noindex. Com a meta presente, a página sai do índice, como mostra o verde da esquerda.
Há uma consequência que vale para o hreflang. Uma página com noindex não deve carregar links de idioma. O Base.astro só emite as tags alternate quando a página não é noindex, e o mesmo vale para o x-default. Se a página saiu do índice, declarar que ela tem uma equivalente em outro idioma seria apontar para um destino que o Google não vai mostrar. A seção 4 explica por que o hreflang só liga páginas indexáveis dos dois lados.
6. Canonical, conteúdo duplicado e conteúdo fino
O canonical é a tag <link rel="canonical" href="..."> que diz qual é a URL preferida de uma página, quando há várias com conteúdo parecido. A documentação o trata como um sinal forte, não como uma ordem, e pede URLs absolutas. No blog, cada página declara a si mesma com o domínio configurado. Isso parece trivial, mas tem uma regra importante: o canonical não serve para apontar a versão em inglês para a portuguesa. A documentação diz que anotações canonical com atributos de idioma não são usadas para canonicalização, e que o Google trata as variantes localizadas como relacionadas, não como duplicadas. Localizações só viram duplicata quando o conteúdo principal continua no idioma original.
Conteúdo duplicado é o caso em que duas URLs servem o mesmo texto, por exemplo uma listagem com a mesma página em duas rotas. O Google decide quais páginas são duplicatas pela similaridade do conteúdo e escolhe uma URL canônica entre elas, consolidando os sinais, como links, nessa escolha. Você pode ajudar com o canonical, mas o Google pode discordar.
Conteúdo fino é a situação em que a página tem pouco texto próprio. No blog, a maioria das páginas finas eram as tags com um ou dois posts, mais as páginas 2 em diante das listagens, que repetiam o formato da primeira com poucas diferenças. A auditoria do OpenSEO marcou 126 páginas com esse problema. Elas ficavam no índice e puxavam para baixo a qualidade média do site.
A decisão foi por volume, e não por idioma. A regra está em isTagIndexable: uma página de tag só entra no índice se for a primeira página e se a tag tiver pelo menos três posts. A constante MIN_INDEXED_TAG_POSTS vale nos dois idiomas, então quando a versão em inglês crescer, as tags voltam a ser indexadas sem configuração extra. O mesmo critério decide o hreflang: uma tag só declara o par com o outro idioma quando a versão do outro lado também é indexável.
O fluxo tem dois losangos, e cada resposta negativa leva ao mesmo destino rosa: noindex e fora do sitemap. A primeira pergunta é se a página é a primeira da listagem. A segunda é se a tag tem três ou mais posts. Só o caminho verde chega a indexável, com canonical próprio, e o último bloco mostra a consequência para o hreflang, que só entra quando o par também é indexável.
A decisão tem um custo que vale registrar. As páginas de série e a página “sobre” continuam indexadas, mesmo quando são finas, porque são páginas que a pessoa pode procurar de propósito. O plano também registrou que a regra precisa ser reavaliada com dados reais do Search Console, e não só com o validador.
Parte 5: dados e domínio
7. Dados estruturados em e Open Graph
Dados estruturados são informações sobre a página em um formato que máquinas leem sem interpretar o texto. O JSON-LD é a forma recomendada pelo Google: um bloco <script type="application/ld+json"> com vocabulário schema.org. A documentação de artigos aceita Article, NewsArticle e BlogPosting, e não exige nenhuma propriedade obrigatória. Ela recomenda headline, image, datePublished, dateModified e author, entre outras.
Em cada post, o blog emite um BlogPosting com headline, description, inLanguage, as datas, author, image e as palavras-chave. A função blogPostingSchema monta esse objeto a partir do frontmatter, e a função jsonLdString troca < por \u003c antes de inserir o JSON no HTML. Essa troca impede que um texto com </script> feche o bloco no meio.
Além do BlogPosting, cada post emite um BreadcrumbList, a trilha de navegação estruturada: Início, a série, se houver, e o título. A documentação exige itemListElement, com pelo menos dois itens, e cada item com position, name e item. Para o último item, o endereço é opcional, mas o blog o inclui, e isso não causa erro.
O Open Graph é outro conjunto de metadados, usado por redes sociais e serviços de prévia, e não aparece nas listas de sinais do Google que consultei. No blog, ele cuida de três coisas: o og:locale com o idioma da página (pt_BR ou en_US), o og:locale:alternate quando existe a versão do outro idioma, e os metadados de artigo, como article:published_time e article:tag. O validador confere que o og:locale bate com o lang do <html>, porque um descompasso entre os dois mandaria uma informação contraditória ao leitor e a qualquer ferramenta que a leia.
A figura mostra a página como fonte de três saídas. A página do post (laranja) gera o BlogPosting e o BreadcrumbList no formato de script, e as metas Open Graph no <head>. Todas caem no validador (verde), que confere a regra do projeto: um único BlogPosting, um BreadcrumbList e um og:locale coerente com o idioma. As setas são curtas porque o caminho é de geração, não de rastreamento.
8. Domínio, Search Console e sitemap
O endereço da página faz parte do conteúdo que o Google indexa. Canonical, hreflang, sitemap, feed RSS e a imagem social carregam o domínio. Se você muda o domínio depois de a página estar no índice, o Google precisa reaprender cada URL, e o caminho recomendado pela documentação de migração é redirecionar as URLs antigas para as novas com , manter os redirecionamentos por pelo menos um ano e atualizar canonical e sitemap.
O blog começou no endereço provisório do CloudFront, e a configuração do site foi trocada para o domínio jeangrijp.com.br. Os canonicals, o hreflang, o sitemap, o robots.txt e o RSS passaram a usar o domínio novo, e o DNS no Route 53 já apontava para a distribuição. O plano registrou que o 301 não seria necessário, porque o endereço do CloudFront continua respondendo e as anotações já apontam para o domínio novo. Essa conclusão depende de o endereço provisório nunca ter sido indexado, e o plano não registra essa checagem. Se o site já tiver sido rastreado pelo endereço antigo, o redirecionamento volta a ser a recomendação oficial.
Para medir o site, o Google pede que você cadastre uma propriedade no Search Console. Ela pode ser por prefixo de URL ou de domínio. A cobre todos os subdomínios e protocolos, e é verificada por um registro TXT no DNS. O Search Console entrega um valor que começa com google-site-verification=, e você o cadastra no seu provedor de DNS. No Route 53, o campo de nome fica vazio ou com @, e o valor do TXT precisa estar entre aspas duplas, conforme a documentação do próprio serviço. A documentação da AWS exige essas aspas, então o valor deve ser digitado assim.
Depois da verificação, envie o sitemap-index.xml no relatório de sitemaps. Esse relatório mostra quando o Googlebot leu o arquivo e se houve erros de processamento. Vale lembrar que os dados não aparecem no mesmo instante. A documentação diz que o registro DNS pode levar de alguns minutos até dois ou três dias para ser servido, e que os dados começam a se acumular “por alguns dias” depois da verificação.
No blog, o registro TXT foi criado no Route 53 em 30 de outubro de 2026 e a propriedade jeangrijp.com.br foi aceita no mesmo dia, depois de o dig já devolver o valor no DNS público. Logo após a verificação, o relatório de desempenho mostrava a mensagem “Dados em processamento: volte em mais ou menos um dia”, sem nenhum dado ainda. Conectar o Search Console ao projeto no OpenSEO ficou para depois desse prazo, porque a inspeção de URLs só tem o que mostrar quando os dados chegam.
A figura separa duas responsabilidades. À esquerda, as mudanças no próprio site (laranja): trocar o domínio na configuração, fazer o canonical e o sitemap usarem o domínio novo, e tratar o endereço antigo. A linha rosa pontilhada entre o domínio novo e o antigo marca o ponto em que a decisão depende de checagem: sem redirecionamento 301, o endereço antigo continua respondendo como uma segunda versão do site. À direita, a verificação no Search Console (azul e laranja, com o último passo verde): o Google entrega o registro, o Route 53 recebe o TXT, a verificação acontece, e só então o sitemap é enviado. A seta animada do último passo representa o envio do sitemap.
Parte 6: validar e evitar armadilhas
9. Como validar
Nenhuma ferramenta enxerga o site inteiro. Cada uma olha um ângulo, e confiar em uma só deixa problemas de fora. O blog usa três, e a figura mostra o que cada uma vê e o que deixa passar.
O OpenSEO audita o site público, como um rastreador faria. Na auditoria de produção, ele encontrou 1 problema crítico (um link interno quebrado), 5 avisos e 5 informativos, entre eles descrições e títulos repetidos, vários H1 e conteúdo fino. Ele não avalia a qualidade do alt, o hreflang nem o JSON-LD. Além disso, não rastreia localhost, então a validação do que ainda não foi para produção precisa de outra ferramenta.
O pnpm check:seo é o validador local, o script scripts/check-seo.mjs. Ele roda depois do build e lê o dist/, que é o HTML que será publicado. Faz sentido confirmar a ordem: o script check do projeto encadeia o astro check, o lint, a formatação, os testes, o build e, por último, o check:seo. Na última execução que rodei, sobre o dist/ gerado hoje, o validador leu 354 páginas e 169 posts, sendo 163 sem tradução, e terminou com 0 erros e 22 avisos, todos sobre o tamanho da description de posts antigos. Ele verifica alt vazio ou igual ao nome do arquivo, JSON-LD válido e presente, og:locale, a igualdade entre hreflang do HTML e do sitemap, a auto-referência, o H1 único, títulos e descrições duplicados, links internos quebrados e translationKey repetido.
O Search Console mostra o que o Google realmente fez: as páginas indexadas, o canonical que ele escolheu, as consultas que trouxeram visitas. É a única fonte que reflete a decisão do Google, e a única que chega com atraso de alguns dias.
A figura organiza as três colunas. Em cada uma, a caixa de cima diz o que a ferramenta é, a caixa do meio o que ela vê (verde) e a de baixo o que ela não vê (rosa). Repare que a qualidade do alt aparece como ponto cego em duas delas, e que o Search Console é o único com dados do próprio Google, com o atraso que isso implica. A leitura correta combina as três: o validador pega a regressão antes do deploy, o OpenSEO confirma o que um rastreador externo enxerga, e o Search Console mostra a resposta final.
10. Lições e armadilhas
A primeira armadilha é o cache de build. O Astro guarda o conteúdo processado dos arquivos Markdown numa base interna, e uma mudança num plugin de remark ou rehype nem sempre a invalida. Há uma issue no repositório do Astro, a #6245, sobre páginas que não se atualizam quando um plugin muda. Ela foi fechada como “não planejada” pelo mantenedor, e as soluções relatadas pela comunidade são apagar a pasta .astro ou rodar o build com --force. Esse risco é direto para este blog, porque o alt dos diagramas é gerado por um plugin: se o SVG muda e o Markdown não, o HTML antigo pode continuar com o alt antigo. Não encontrei nenhum caso desse problema registrado no blog, e a aula não afirma que ele aconteceu aqui. Quando uma correção de alt ou de hreflang não aparecer no dist/, o primeiro teste é reconstruir com --force.
A segunda armadilha é o falso positivo, ou o falso negativo, do validador. A regra que recusa alt igual ao nome do arquivo deixa passar qualquer alt com quatro palavras ou mais, porque presume que um título desse tamanho é um título de verdade. Isso é uma escolha consciente, mas significa que um alt de quatro palavras que apenas repete o arquivo passa sem aviso. Outro ponto cego está no script que lê a description dos posts: ele procura uma linha única que começa com description:. Se a description estiver em YAML de várias linhas, o validador não a vê e não avisa. Não encontrei no repositório um falso positivo observado. Os dois pontos acima são limites do código, e vale conferi-los quando um aviso parecer estranho ou quando um problema real passar em branco.
A terceira armadilha é a combinação de Disallow com noindex, já discutida na seção 5. Ela é fácil de cometer porque cada linha parece correta isoladamente. O antídoto é decidir uma única ferramenta por objetivo: noindex para tirar da busca, robots.txt para controlar o rastreamento de recursos que não precisam ser lidos, e nunca as duas para a mesma página.
A quarta lição é sobre a ordem do plugin de títulos. O plugin que rebaixa os H1 precisa rodar depois dos plugins que procuram H2 na estrutura original. O código documenta isso num comentário, e a ordem está no astro.config.mjs. Trocar essa ordem sem pensar faz o sumário e outros recursos deixarem de achar as seções.
Por fim, o caso do blog mostra que correções de SEO são fases, não um arquivo. O plano separou decisões (idioma, domínio, formato do alt), correções e verificações, e marcou cada tarefa como concluída só depois de conferir o resultado no dist/. Essa é a parte que você pode levar para qualquer site estático: decidir antes de codificar e verificar no artefato gerado, não no código-fonte.
Exercícios
- Abra o
dist/sitemap-0.xmldo seu projeto e conte quantas URLs têmxhtml:link. Depois confira, para um post traduzido, se o conjunto de links do HTML é igual ao do sitemap. Se não for, descubra qualtranslationKeyestá faltando. - Escreva um robots.txt com
Disallow: /offline/e outro com a meta noindex na página de/offline/. Explique, sem consultar esta aula, por que o segundo é o correto para tirar a página do índice. - Pegue um SVG de diagrama do seu projeto e rode a lógica de
buildAltsobre o título e a descrição dele. Veja se o alt resultante descreve a figura para quem não a vê. Se não descreve, reescreva o título do SVG, que é a fonte do texto. - Liste as páginas de tag do seu blog com menos de três posts e diga, para cada uma, se ela deveria ter noindex e por quê. Depois compare sua resposta com a regra de
isTagIndexable.
Glossário
- Rastreamento (crawling): o Googlebot baixar o HTML e os recursos de uma URL.
- Indexação: o Google analisar uma página e guardá-la no índice, que é a base de onde a busca responde.
- Ranking: a ordenação dos resultados de busca por relevância, feita na etapa de entrega.
- alt: texto alternativo de uma imagem, usado quando ela não carrega, por leitores de tela e pelo Google.
- hreflang: anotação que declara a versão de idioma ou de região de uma página e as suas equivalentes.
- x-default: valor de hreflang para a página usada quando nenhum idioma combina com o do visitante.
- Canonical: tag que indica a URL preferida entre páginas com conteúdo parecido.
- noindex: diretiva na página que pede ao Google para não mostrá-la na busca.
- robots.txt: arquivo na raiz do domínio com instruções de rastreamento.
- Sitemap: lista de URLs que o site quer que o Google conheça.
- JSON-LD: formato de dados estruturados em um bloco
<script>, com vocabulário schema.org. - Propriedade de domínio: cadastro no Search Console que cobre todo o domínio, verificado por registro DNS.
- 301: código de redirecionamento permanente de uma URL antiga para uma nova.
Fontes
Consultadas em 30 de outubro de 2026.
- Google Search Central: como a Pesquisa funciona. Três etapas (rastreamento, indexação e entrega dos resultados), indexação não garantida e descoberta por links e sitemaps.
- Google Search Central: metatag robots e noindex. A regra noindex só é lida se o rastreamento for permitido, e o bloqueio no robots.txt faz as regras de indexação serem ignoradas.
- Google Search Central: versões localizadas e hreflang. Retorno recíproco, auto-referência, URLs absolutas, equivalência entre head, cabeçalho HTTP e sitemap, e
x-default. O sitemap precisa listar todas as alternativas, incluindo a própria página. - Google Search Central: imagens. Alt descritivo no contexto da página, sem palavras-chave em excesso, e o nome do arquivo como pista leve. A página não cita
<desc>e não diz se o texto dentro de um SVG usado em<img>é lido. - Google Search Central: títulos. Um título por página, descritivo, sem texto padrão repetido e no idioma do conteúdo.
- Google Search Central: snippets e meta description. O Google pode substituir a description, e a página não define limite de tamanho.
- Google Search Central: canonical e URLs duplicadas. Canonical como sinal forte, URLs absolutas, consolidação por similaridade de conteúdo e uso de canonical em variantes de idioma.
- Google Search Central: dados estruturados de artigo. BlogPosting é um tipo aceito, não há propriedades obrigatórias e as recomendadas são headline, image, datePublished, dateModified e author.
- Google Search Central: breadcrumb.
itemListElementobrigatório, com ao menos dois itens, eitemopcional no último. - Google Search Central: criar sitemap. Índice de sitemaps, envio pelo relatório e o limite de 50 mil URLs ou 50 MB.
- Google Search Central: mudança de URLs e domínio. Redirecionamentos 301 permanentes, canonical e sitemap atualizados e a recomendação de mantê-los por pelo menos um ano.
- Search Console: verificar propriedade de domínio. Registro TXT no DNS, campo de nome em branco ou
@, prazo de propagação de dois ou três dias e “alguns dias” para os dados começarem a aparecer. - AWS: tipos de registro DNS no Route 53. Valores TXT são strings entre aspas duplas, com limite de 255 caracteres por string.
- Issue #6245 do Astro: páginas não atualizam ao mudar plugins de remark ou rehype. Fechada como “não planejada” pelo mantenedor, com as soluções relatadas pela comunidade.
Medido no repositório do blog, na data da consulta: os trechos de Base.astro, robots.ts, sitemap-links.mjs, sitemap-noindex.mjs, structured-data.ts, rehype-diagrams.mjs, rehype-demote-h1.mjs, indexing.ts, LangSwitch.astro e scripts/lib/seo-check.mjs foram lidos, e a função buildAlt foi executada sobre um SVG real do blog. O pnpm check:seo foi rodado sobre o dist/ existente, gerado em 30 de outubro de 2026, com o resultado descrito na seção 9. Não rodei o build completo nem o Lighthouse, e não consultei o OpenSEO nem o Search Console, por dependerem de conta e de créditos.
Não consegui confirmar: se o endereço provisório do CloudFront chegou a ser indexado (isso decide se o 301 era necessário); quanto tempo o Search Console leva ao todo para mostrar dados no blog (a mensagem inicial fala em cerca de um dia, e o prazo real ainda não foi medido); se o cache do Astro causou algum problema real neste projeto; e se o limite de 80 a 160 caracteres para o alt melhora o resultado, já que nenhuma das fontes consultadas define um tamanho ideal.
Relacionados
- ARIA
- SSG
- CDN
- Amazon CloudFront
- Route 53
- S3
- AWS