Pular para o conteúdo

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

Tamanho da fonte:
Largura da coluna:
Tom:
Espaço entre linhas:
Fonte:

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

ParteSeçõesPergunta que responde
1. Como o Google encontra a página1Quais são as etapas entre uma URL existir e aparecer na busca?
2. O que existe dentro da página2 e 3Que textos o Google lê, e como o alt dos diagramas é montado?
3. Mais de um idioma4Como PT e EN se declaram sem confundir o Google?
4. O que entra e o que sai do índice5 e 6Quando usar sitemap, robots.txt, noindex e ?
5. Dados e domínio7 e 8Que dados estruturados e que domínio a página declara?
6. Validar e evitar armadilhas9 e 10Que 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.

Caminho de uma URL: o Googlebot a rastreia, o robots.txt pode bloqueá-la, a indexação lê o HTML e o noindex decide se entra no índice da busca
figura 1 · Caminho de uma URL do até a entrega no Google

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.

Hierarquia de títulos antes e depois do plugin rehype-demote-h1: o H1 do cabeçalho fica único e os h1 do Markdown descem para h2 e h3
figura 2 · Hierarquia de títulos de um post antes e depois do rebaixamento do H1

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 ![3 01 chamada](./assets/1-3-01-chamada.svg), 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 plugin lê título e descrição do SVG no build e monta o alt de até 160 caracteres, que é o único texto da figura que o Google lê
figura 3 · Como o alt de um diagrama SVG chega ao Google

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.

Post em português e em inglês se declaram mutuamente no head e no sitemap, com auto-referência; um post sem tradução não leva hreflang
figura 4 · Pareamento hreflang entre um post em português e a versão em inglês

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.

Árvore de decisão: para tirar uma página do Google, use noindex sem bloquear o rastreio no robots.txt, porque o Google precisa rastrear para ler a regra
figura 5 · Decidir entre noindex e bloqueio no robots.txt para uma página

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.

Quando uma página de tag entra no índice: a primeira página com pelo menos três posts é indexável, e as demais ou as finas recebem noindex
figura 6 · Quando uma página de tag entra no índice do Google

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.

Página de post com BlogPosting, BreadcrumbList e metas Open Graph de idioma e artigo, conferidas pelo validador local a cada build
figura 7 · Dados estruturados e Open Graph de uma página de post

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.

Passos para trocar o domínio do blog e verificá-lo no Google Search Console por registro TXT no Route 53 antes de enviar o sitemap
figura 8 · Troca de domínio e verificação no Google Search Console

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.

Três ferramentas, três visões: OpenSEO audita o site público, o check:seo lê o dist/ e o Search Console mostra dados reais; cada uma deixa algo de fora
figura 9 · O que cada ferramenta de SEO enxerga no blog

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

  1. Abra o dist/sitemap-0.xml do seu projeto e conte quantas URLs têm xhtml:link. Depois confira, para um post traduzido, se o conjunto de links do HTML é igual ao do sitemap. Se não for, descubra qual translationKey está faltando.
  2. 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.
  3. Pegue um SVG de diagrama do seu projeto e rode a lógica de buildAlt sobre 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.
  4. 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.

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

↑ Voltar ao topo

Atalhos de teclado

Abrir busca
/
Alternar tema claro/escuro
t
Aula anterior na série
←
Próxima aula na série
→
Lista de atalhos
?