Sunset de hostname sem envenenar o Search Console
Aposentar um produto tem um passo que quase nenhum checklist de sunset inclui: o que fazer com o hostname, o endereço público do produto. A omissão dele não avisa na hora. Avisa semanas depois, no relatório de indexação do seu domínio inteiro.
O caso que gerou esta nota: um produto saiu do ar em 2026-08-14. Containers derrubados, backup final guardado, documentação atualizada. Sunset exemplar, exceto por um detalhe.
O registro DNS de keybelt.haruo.dev continuou existindo. E o hostname continuou configurado no ingress, a lista de rotas do Cloudflare Tunnel, apontando para um origin (o servidor de destino) que não existia mais. Resultado: todo acesso ao subdomínio virou 502, o erro de servidor. Para um humano, irrelevante: o produto não tinha mais usuários. Para o Googlebot, outra história.
Por que o 502 de um subdomínio contamina o domínio
Três mecanismos se combinam, e vale entender cada um:
1. A propriedade sc-domain cobre todos os subdomínios. O Search Console é o painel do Google sobre o seu site. Quem verifica o domínio lá como propriedade de domínio (sc-domain:haruo.dev) ganha relatórios agregados de tudo: site institucional, blog e qualquer subdomínio, vivo ou morto. Quatro dias depois do sunset, o relatório de cobertura do domínio listava 7 páginas em “Server error (5xx)”. Todas do produto morto, todas poluindo o painel do site que importa.
2. 5xx é o pior estado terminal possível. Para o Google, erro de servidor é transitório por definição: ele repete o crawl e segura as URLs no índice por meses, esperando o servidor voltar. Um 404 solta a URL com o tempo; um 410 solta mais rápido; um 301 transfere. O 502 não solta nunca. O hostname órfão vira uma ferida que não cicatriza, reaberta a cada recrawl.
3. O robots.txt antigo trava a saída. O produto morto tinha Disallow: /api no robots.txt, o arquivo que diz ao buscador onde ele não pode entrar. Com o site fora do ar, esse robots não é mais servido. Mas o Google se lembra dele, e as URLs sob /api seguem em “Blocked by robots.txt” (e uma em “Indexed, though blocked by robots.txt”). O detalhe perverso: enquanto o robots não liberar, o Google não recrawleia essas URLs. E portanto nunca descobre que elas morreram. O bloqueio que protegia a API viva agora impede o enterro da API morta.
Resumindo o estado: 7 URLs em 5xx eterno, 2 presas atrás de um robots fantasma, 1 indexada e bloqueada. Nenhuma delas com um caminho natural para sair do índice.
A correção: um Worker de 56 linhas no hostname morto
A correção não exigiu tocar DNS nem o ingress do túnel. Isso importa, porque na hora do incidente o token disponível do wrangler, a CLI da Cloudflare, só tinha permissão de leitura na zona. Um Cloudflare Worker com rota de zona (keybelt.haruo.dev/*), isto é, um script hospedado pela própria Cloudflare, intercepta as requisições na borda, antes de chegarem ao túnel. O DNS continua apontando para o túnel; a rota captura primeiro.
O Worker faz exatamente três coisas:
// /robots.txt → 200, texto permissivo
"User-agent: *\nAllow: /"
// /api e /api/* → 410 Gone
"410 Gone — a API foi descontinuada em 2026-08-14."
// todo o resto → 301 para o site principal
Response.redirect('https://haruo.dev/', 301);
Cada linha resolve um dos três mecanismos:
- O robots permissivo servido de verdade (200,
text/plain, cache de 1h) destrava o recrawl: o Google volta a poder olhar as URLs que o robots antigo escondia. E só então consegue ver o que aconteceu com elas. - O 410 nas rotas de API diz o que o 502 nunca diria: isto morreu de propósito, pare de voltar. Gone é o sinal mais forte de remoção que o protocolo tem. É o que faz a URL sair do índice em dias, não meses.
- O 301 no resto aproveita o que sobrou. Qualquer link externo, favorito ou menção antiga passa a apontar para o site vivo. Isso transfere o pouco de sinal que o subdomínio acumulou, em vez de queimá-lo em erro.
Nada disso exige o produto de volta, um servidor ou um deploy no pipeline do site. O Worker é um artefato à parte, deployado manualmente, fora do build. E a ordem de desmontagem final ficou registrada junto: validar nos relatórios do Search Console que as URLs sumiram. Só então apagar, nesta ordem, o registro DNS → o hostname no ingress do túnel → o próprio Worker. Desmontar antes de validar é recriar o problema para economizar três recursos que não custam nada parados.
O contraste: o sunset que não criou órfão
Cinco dias depois, outro produto nosso saiu da mesma VPS. O runbook desse segundo sunset mostra o que se aprende com o primeiro: o hostname nunca ficou órfão porque nunca ficou sem origin.
O produto (um tradutor com ~700 expressões em banco) virou export estático. A base foi exportada para um JSON versionado. A mesma função de tradução que rodava no servidor passou a rodar no browser sobre esse JSON, com paridade verificada caso a caso antes do corte. E o site foi para o Cloudflare Pages.
O passo crítico do runbook é a troca atômica de dono do hostname. Ou seja: remover o public hostname do túnel e, no mesmo passo, registrar o custom domain no projeto Pages. O subdomínio saiu de “aponta para um container na VPS” para “aponta para o Pages” sem janela de 5xx. E os containers só foram derrubados depois da verificação (curl -sI no hostname, status 200, headers certos, dados servidos).
Dois detalhes desse runbook que valem roubar:
- O volume do banco sobrevive 7 dias após o corte, com o rollback documentado (subir o compose de volta e recriar o hostname no túnel). Sunset com botão de arrependimento barato é sunset que se executa sem drama.
- A perda aceita foi escrita antes do corte: o preview de link social deixou de ter imagem, porque export estático não gera OG dinâmico, a imagem montada na hora. Perda conhecida e aceita é diferente de surpresa. A diferença é uma linha no runbook.
O checklist que ficou
Todo sunset de produto com hostname próprio, na ordem:
- Decida o destino do hostname antes de derrubar qualquer coisa. Ou ele ganha um novo origin (estático, redirect, Pages), ou ganha um Worker de lápide. “Fica sem nada” não é opção: sem nada é 502, e 502 é eterno.
- Se for lápide: robots permissivo + 410 nas rotas de máquina + 301 no resto. O robots destrava o recrawl; o 410 enterra; o 301 aproveita. Um Worker com rota de zona faz os três sem tocar DNS.
- Se for migração: troque o dono do hostname atomicamente e verifique o novo origin respondendo antes de derrubar o antigo.
- Valide no Search Console antes de desmontar a lápide. As URLs precisam sair dos relatórios; só então DNS → ingress → Worker, nessa ordem.
- Escreva o rollback e a perda aceita no runbook, com prazo de arrependimento explícito.
O custo de tudo isso é uma hora. O custo de pular foi um relatório de indexação poluído por semanas, no domínio que carrega o site principal. Com propriedade de domínio no Search Console, não existe “subdomínio que não importa”. Todo hostname que já respondeu 200 um dia é uma promessa feita ao crawler, e sunset é o ato de desfazer a promessa educadamente.
Precisa de um projeto técnico sob medida?
Arquitetura, TypeScript, APIs e automação, do protótipo à produção. Quem responde o seu e-mail é quem escreve o código, e o prazo que eu prometo é o prazo que eu consigo cumprir.
Me manda uma mensagem →