A republicação pode terminar com sucesso e ainda abrir uma página quebrada: HTML novo pede um chunk antigo que já não existe, CSS antigo estiliza componentes novos ou um service worker continua respondendo com a versão anterior.

O sintoma parece aleatório porque diferentes pessoas, regiões e perfis guardam cópias diferentes. Criar outro slug ou pedir Ctrl+Shift+R transfere o problema para o público; não corrige o contrato de versão.

Este guia mostra como localizar a camada stale, publicar um pacote coerente e provar que visitantes novos e recorrentes recebem a mesma versão.

Separe as camadas antes de culpar o cache

Uma landing page pode ter cópias em pelo menos cinco pontos:

  1. pasta de build local;
  2. pacote enviado para publicação;
  3. cache de CDN ou intermediário;
  4. cache HTTP do navegador;
  5. CacheStorage controlado por service worker.

Há ainda o back-forward cache do navegador, que preserva uma página inteira para navegação de ida e volta. Ele não é a mesma coisa que o cache HTTP.

Registre o caminho observado:

fonte e commit:
comando de build:
pasta de saída:
hash do pacote:
Site ID e slug:
deploy ID:
URL e domínio:
arquivo esperado:
bytes ou marcador esperado:
headers observados:
service worker e escopo:

Sem essa cadeia, “a versão antiga voltou” mistura hipóteses demais.

Crie um marcador de versão verificável

Antes de republicar, escolha uma evidência que não dependa apenas da aparência:

  • hash do commit no HTML;
  • identificador da build em uma meta tag;
  • nome versionado do bundle;
  • texto específico aprovado;
  • manifesto de arquivos com hashes.

Não exponha segredo, token, caminho interno ou dado pessoal. O marcador deve permitir responder “qual versão chegou?”, não revelar como acessar o ambiente.

Uma meta tag simples pode ajudar:

<meta name="build-version" content="2026-09-07.1" />

O valor precisa vir do processo de build e acompanhar o pacote. Editar a tag manualmente depois da geração pode criar outra divergência.

Faça os nomes acompanharem os bytes

O padrão mais robusto é gerar um novo caminho quando o conteúdo muda:

assets/app.91d22a.js
assets/styles.b73e10.css
assets/hero.045aca.webp

O HTML da mesma build referencia exatamente esses nomes. Depois de uma mudança, a build produz novos arquivos e outro HTML:

assets/app.c430ab.js
assets/styles.11e05d.css

A orientação de cache HTTP da MDN recomenda URLs versionadas para conteúdo com max-age longo. Enquanto o arquivo antigo mantém os mesmos bytes, ele continua seguro para documentos antigos que ainda o referenciam.

Evite:

  • sobrescrever app.js sob TTL longo;
  • usar query string fixa como ?v=1 em todas as builds;
  • renomear só o JavaScript e deixar CSS ou imagens estáveis;
  • calcular o nome antes da transformação final;
  • misturar saída de duas execuções na mesma pasta;
  • editar arquivos dentro de dist depois do build.

Gere a saída em diretório limpo

Uma build incremental mal configurada pode manter chunks órfãos e fazer o pacote parecer completo. Antes de publicar:

  1. use o comando de build oficial;
  2. confirme que a saída foi recriada pelo comando;
  3. liste HTML, CSS, JavaScript, imagens, fontes e manifests;
  4. resolva cada referência local;
  5. procure arquivos ausentes e assets inesperados;
  6. compacte apenas a saída publicável atual.

Não apague diretórios fora do contrato do projeto. “Limpar a build” significa usar a tarefa prevista pelo framework ou gerar em uma saída isolada, não executar remoção ampla por suposição.

Uma verificação focada pode procurar referências:

Terminal window
rg -n 'src=|href=|url\(|import\(' dist

Complemente com o build e os testes do projeto. Encontrar texto não garante que todos os imports dinâmicos estejam cobertos.

Publique a Session como uma Version

No Site no Ar, a Session guarda as alterações feitas pelas file tools. O deploy publica o diretório public/, cria uma Version e ativa o conjunto depois que ele está pronto.

write e edit mudam somente os caminhos indicados; um chunk, uma rota ou um service worker antigo continua no Site até ser removido explicitamente com rm no bash, seguido de deploy. Revise a Session inteira antes de publicar a Version.

Para uma landing page com assets, mantenha em public/:

index.html
assets/app.c430ab.js
assets/styles.11e05d.css
assets/hero.045aca.webp

O contrato de Publicação exige public/index.html. O guia de republicação do mesmo Site mostra como preservar Site ID, slug, URL e domínios enquanto a versão muda.

Antes de publicar, compare a saída atual com o estado desejado. No upload completo, confira se o pacote inclui todos os arquivos que devem permanecer. No fluxo incremental do MCP, liste os arquivos com ls no bash, remova cada caminho obsoleto com rm e atualize public/sitemap.xml quando uma rota HTML sair. Só então execute deploy. A remoção muda o conteúdo público; confirme os caminhos exatos e verifique o status terminal do deploy. Não remova public/index.html: publique uma substituição.

Não publique HTML primeiro e assets depois por caminhos independentes. Mesmo uma janela curta permite que o documento novo aponte para um arquivo que ainda não está disponível.

Não sobrescreva assets imutáveis

Se uma resposta usa:

Cache-Control: public, max-age=31536000, immutable

o contrato é que aquela URL não mudará enquanto estiver fresca. Corrigir os bytes sob o mesmo nome não força navegadores a buscá-los novamente.

A solução é:

  1. gerar outro nome para o arquivo corrigido;
  2. gerar HTML que aponta para esse nome;
  3. publicar o pacote completo;
  4. confirmar a URL nova na resposta pública.

O artigo sobre como configurar Cache-Control detalha quando usar revalidação e quando um TTL longo é seguro.

Verifique HTML antes dos subrecursos

O HTML é o mapa da versão. Se ele ainda aponta para app.91d22a.js, limpar somente o cache do JavaScript não fará surgir app.c430ab.js.

Baixe e examine a resposta:

Terminal window
curl -sS -L \
-H 'Accept: text/html' \
-H 'Sec-Fetch-Dest: document' \
-w '\nURL final: %{url_effective}\n' \
https://exemplo.com/ \
-o /tmp/landing-index.html
rg -n 'build-version|script|stylesheet|assets/' /tmp/landing-index.html

Depois consulte cada asset referenciado:

Terminal window
curl -sS -L -D - -o /dev/null \
-w '\nURL final: %{url_effective}\n' \
https://exemplo.com/assets/app.c430ab.js
curl -sS -L -D - -o /dev/null \
-w '\nURL final: %{url_effective}\n' \
https://exemplo.com/assets/styles.11e05d.css

Use a URL final e o último bloco de headers para confirmar status, Content-Type, Cache-Control, ETag, Age e o header de cache da CDN quando disponível. Um 200 text/html para uma URL .js é fallback ou erro mascarado, não asset válido.

Use validadores para distinguir cópia de versão

ETag e Last-Modified permitem revalidação condicional. Quando o cliente envia If-None-Match, a origem pode responder 304 se a representação ainda corresponde ao validador.

Compare uma requisição sem condicional com outra usando o valor observado. Não invente o ETag e não presuma que ele é sempre um hash de conteúdo: o RFC trata a tag como um validador opaco.

Também registre:

  • Age maior que zero, que indica tempo em cache intermediário;
  • CF-Cache-Status ou equivalente;
  • redirect para outro hostname;
  • diferença entre URL padrão e domínio próprio;
  • parâmetros de query presentes no HTML real.

A resposta precisa ser ligada à URL e ao horário do teste. Headers copiados de outra rota não provam o comportamento do documento atual.

Trate service worker como uma origem local

Um service worker pode interceptar requisições e devolver respostas de CacheStorage sem consultar a rede. Por isso, headers da CDN podem estar corretos enquanto a página continua antiga em perfis que instalaram uma versão anterior.

A especificação atual de Service Workers do W3C mantém estados separados para worker em instalação, em espera e ativo. Um worker novo não assume automaticamente todas as páginas já abertas.

Revise:

  • URL e escopo do script;
  • updateViaCache;
  • nomes dos caches por versão;
  • lista de precache;
  • limpeza de caches antigos no activate;
  • uso de skipWaiting() e clients.claim();
  • comportamento de abas já abertas;
  • fallback offline;
  • resposta quando um asset saiu do manifesto.

Não adicione skipWaiting() apenas para “forçar atualização”. Ele pode trocar o controlador enquanto uma página carregada ainda espera outra versão dos assets. A decisão precisa considerar compatibilidade entre HTML, worker e bundles.

Diagnostique em ordem

Quando uma versão antiga aparece, siga esta sequência:

1. Prove a fonte e a build

O marcador correto existe na fonte autorizada e no arquivo dentro de dist?

2. Prove os arquivos do Site

public/index.html referencia os mesmos nomes de asset que existem no workspace?

3. Prove o deploy

O deploy terminou em sucesso no Site ID esperado? A URL e o slug foram preservados?

4. Prove a resposta de rede

Um cliente HTTP sem estado recebe o HTML novo e os assets corretos?

5. Prove os caches HTTP

Navegação normal recebe a versão nova? O HTML revalida? Assets novos têm URLs novas?

6. Prove o service worker

DevTools mostra worker ativo? A resposta veio de ServiceWorker? O cache antigo foi removido pela versão correta?

7. Compare domínios e regiões

URL padrão e domínios próprios mostram o mesmo marcador? Há redirect ou proxy com política diferente?

Mude uma variável por vez. Limpar todos os dados do navegador antes de registrar a camada elimina a evidência que mostraria a causa.

Teste visitantes novos e recorrentes

Use uma matriz pequena:

Cenário Perfil Resultado esperado
primeira visita limpo HTML e assets da versão atual
retorno já visitou HTML descobre a versão atual
aba antiga aberta antes do deploy comportamento definido e recuperável
reload comum já visitou nenhuma mistura de versões
offline com service worker versão e fallback documentados
domínio próprio limpo e recorrente mesmo marcador da URL padrão

Percorra também interações que carregam chunks sob demanda. A home pode parecer correta enquanto formulário, modal ou rota secundária ainda pede um arquivo removido.

Faça rollback com outra versão coerente

Rollback não é editar um asset direto na CDN. Recupere a fonte aprovada, gere uma saída limpa e publique outro pacote completo no mesmo Site.

Registre:

versão com problema:
sintoma:
camada causal:
versão restaurada:
deploy de rollback:
HTML observado:
assets observados:
service worker observado:
URLs verificadas:

Se o problema veio de um asset imutável sobrescrito, o rollback precisa usar outra URL. Restaurar os bytes sob o caminho já armazenado deixa parte dos visitantes presa à cópia anterior.

Prompt copiável para o agente

Diagnostique por que esta landing page mostra assets antigos sem criar outro Site.
Fonte e build: [caminhos]
Site ID, slug e URL: [valores]
Versão esperada: [marcador]
- Compare fonte, saída, pacote, deploy e resposta pública.
- Leia o HTML primeiro e liste exatamente os assets referenciados.
- Confirme status, Content-Type, Cache-Control, ETag, Age e cache da CDN.
- Verifique URL padrão e domínios próprios.
- Inspecione service worker, escopo, caches e ciclo de atualização.
- Não use hard reload como única prova e não crie outro slug.
- Corrija nomes versionados ou política na fonte responsável.
- Republique um pacote completo no mesmo Site.
- Teste perfis novos, recorrentes e abas antigas antes do handoff.

Checklist final

  • fonte, build e pacote possuem o mesmo marcador;
  • mudança de bytes gera outra URL de asset;
  • HTML referencia somente assets da própria build;
  • deploy terminou no Site ID correto;
  • URL padrão e domínios próprios foram comparados;
  • status, tipo, cache e validadores foram registrados;
  • assets ausentes não recebem fallback HTML silencioso;
  • service worker e CacheStorage foram inspecionados;
  • visitantes novos e recorrentes recebem versão coerente;
  • chunks tardios e jornadas críticas foram testados;
  • rollback gera outro pacote completo;
  • nenhum novo slug foi usado para esconder o defeito.

Uma republicação confiável não termina quando os novos arquivos chegam ao servidor. Ela termina quando o HTML público aponta para uma única versão, os caches conseguem descobrir essa versão e perfis antigos não combinam peças de builds diferentes.