Um site criado por IA pode carregar rápido na primeira publicação e continuar mostrando uma versão antiga depois da próxima. O problema raramente é “o cache” como uma coisa única. HTML, JavaScript, CSS, imagens, CDN e navegador obedecem a contratos diferentes.

Cache-Control define parte desse contrato. A configuração segura não é colocar o maior max-age em tudo: é decidir quais respostas podem ser reutilizadas, por quanto tempo e como o cliente descobre que uma nova versão existe.

Este guia mostra como classificar respostas, versionar assets, escolher diretivas e validar a URL publicada sem usar recarregamento forçado como única evidência.

Entenda o que o cache reutiliza

Um cache guarda uma resposta associada a uma chave. Quando outra requisição compatível chega, ele pode:

  • devolver a resposta armazenada enquanto ela está fresca;
  • revalidar a cópia com a origem;
  • receber 304 Not Modified e reutilizar o corpo local;
  • baixar uma nova resposta;
  • recusar o armazenamento por causa das diretivas ou do contexto.

O RFC 9111 define frescor, validação, invalidação e as diretivas de cache HTTP. Ele também diferencia cache privado, como o navegador de uma pessoa, de cache compartilhado, como um intermediário ou CDN.

Não confunda três ações:

Ação O que significa
armazenar manter uma cópia da resposta
reutilizar atender outra requisição com essa cópia
revalidar perguntar à origem se a cópia ainda representa o recurso

no-cache permite armazenamento, mas exige validação antes da reutilização. no-store pede que a resposta não seja armazenada. Essa diferença muda tanto desempenho quanto privacidade.

Faça um inventário por classe de resposta

Antes de escrever uma política, liste o que o site realmente entrega:

URL ou padrão:
tipo de conteúdo:
personalizada por usuário?
usa cookie ou Authorization?
muda sem mudar a URL?
nome possui hash ou versão?
cache privado permitido?
cache compartilhado permitido?
tempo aceitável de versão antiga:
validador observado:

Separe pelo menos:

  • documentos HTML;
  • CSS e JavaScript gerados pela build;
  • imagens, fontes e outros assets;
  • robots.txt, sitemap e manifest;
  • respostas de API;
  • páginas com cookie, sessão ou dados pessoais;
  • recursos de terceiros;
  • respostas de erro e redirects.

Uma extensão não prova a política. Verifique Content-Type, status e corpo. O guia de nosniff e tipos MIME ajuda a detectar quando uma URL de script recebe HTML ou outro formato inesperado.

Diferencie as diretivas principais

Use cada diretiva por sua semântica, não por uma receita universal:

Diretiva Efeito principal
max-age=N mantém a resposta fresca por N segundos no cliente
s-maxage=N define frescor para caches compartilhados
public permite armazenamento em cache compartilhado quando o restante do contrato também permite
private restringe a resposta a cache privado
no-cache exige revalidação antes de reutilizar
no-store pede que a resposta não seja armazenada
must-revalidate proíbe reutilizar a resposta stale sem validação bem-sucedida
immutable declara que a URL não mudará enquanto estiver fresca
stale-while-revalidate=N permite servir stale por uma janela enquanto revalida em segundo plano

stale-while-revalidate troca atualidade imediata por menor latência. O RFC 5861 permite servir a cópia stale durante a janela declarada enquanto uma revalidação assíncrona acontece. Não aplique isso a preço, disponibilidade, instrução urgente ou conteúdo cuja versão antiga cause dano.

Evite combinações contraditórias copiadas de snippets, como no-store, public, max-age=31536000. A política precisa expressar uma decisão compreensível para quem fará o próximo deploy.

Use políticas diferentes para HTML e assets

O padrão mais previsível para um site estático separa a página que aponta para a versão dos arquivos que compõem essa versão.

HTML com URL estável

Rotas como /, /produto/ e /obrigado/ precisam continuar no mesmo endereço depois da republicação. Como a URL não muda, o navegador precisa consultar a origem para descobrir o HTML novo.

Uma política de revalidação pode ser:

Cache-Control: no-cache
ETag: "versao-do-documento"

no-cache não significa “baixar o corpo inteiro sempre”. Com um ETag coerente, a requisição pode usar If-None-Match e receber 304 quando nada mudou. A documentação de requisições condicionais da MDN detalha esse fluxo.

Se o HTML for personalizado ou carregar dados privados, acrescente as restrições adequadas, como private, ou use no-store quando a resposta não deve permanecer armazenada.

Assets com nome versionado

Quando uma mudança de bytes gera outra URL, o arquivo antigo pode ter vida longa:

/assets/app.a81f3c.js
/assets/styles.725bd1.css
/assets/hero.51ac90.webp

E a resposta pode declarar:

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

Esse padrão só é correto se o mesmo caminho nunca receber outros bytes durante o período. Alterou o conteúdo, altere o nome e atualize o HTML. A orientação de cache HTTP da MDN chama esse processo de cache busting.

Assets com nome estável

Arquivos como /logo.png, /manifest.webmanifest ou /robots.txt podem mudar sem trocar de URL. Não combine nome estável com immutable longo. Use revalidação ou um max-age alinhado ao tempo máximo aceitável de desatualização.

Trate respostas privadas como outra fronteira

Não marque uma resposta como public apenas porque ela passa por uma CDN. Revise:

  • Set-Cookie na resposta;
  • Authorization na requisição;
  • conteúdo calculado por usuário, organização ou permissão;
  • parâmetros que mudam o corpo;
  • páginas de confirmação com dados pessoais;
  • links assinados ou temporários;
  • respostas de erro que revelam contexto interno.

Para conteúdo personalizado, private evita armazenamento compartilhado, mas ainda permite cache no navegador. Para respostas que não devem permanecer armazenadas, use no-store.

Cache não é controle de acesso. Uma URL pública continua pública mesmo quando a resposta usa no-store, e uma URL privada continua precisando de autenticação e autorização.

Configure Vary somente quando a representação muda

Quando o corpo depende de um header de requisição, Vary inclui essa dimensão na seleção da resposta armazenada. O RFC 9111 exige que os campos indicados coincidam antes de reutilizar a representação.

Exemplos possíveis:

Vary: Accept-Encoding
Vary: Accept-Language

Não adicione Vary: * nem uma lista ampla “por segurança”. Cada dimensão reduz o reaproveitamento e aumenta a quantidade de variantes. Se o conteúdo depende de cookie ou autorização, a solução pode ser impedir cache compartilhado, não transformar valores altamente variáveis em chave.

Entenda o contrato da plataforma

O contrato de Publicação do Site no Ar envia HTML e assets como uma versão coerente. Uma republicação prepara outro conjunto e só então ativa a nova versão do mesmo Site. Isso reduz a chance de apontar para um pacote parcialmente enviado, mas não apaga cópias já armazenadas no navegador.

O runtime de static assets usado pela plataforma pode fornecer validadores e políticas padrão. A documentação atual de headers de Static Assets da Cloudflare descreve Cache-Control: public, max-age=0, must-revalidate e ETag como defaults. Ainda assim, o contrato que vale para uma publicação é a resposta observada na URL final, porque transformação de HTML, domínio, proxy e configuração podem alterar os headers.

O upload do Site no Ar não interpreta um arquivo _headers dentro do pacote como configuração da resposta. Para ativar immutable, private, no-store ou outra política diferente dos defaults, você precisa de uma origem ou proxy autorizado que controle esses headers, ou de suporte explícito da plataforma. Sem essa camada, mantenha a proposta registrada e trate a resposta pública observada como o contrato real.

O pacote enviado não deve ser tratado como prova de uma política. Confirme a resposta pública antes de dizer que qualquer diretiva está ativa.

Valide sem depender de um único reload

Inspecione documento e assets separadamente:

Terminal window
curl -sS -L -D - -o /dev/null \
-H 'Accept: text/html' \
-H 'Sec-Fetch-Dest: document' \
-w '\nURL final: %{url_effective}\n' \
https://exemplo.com/
curl -sS -L -D - -o /dev/null \
-w '\nURL final: %{url_effective}\n' \
https://exemplo.com/assets/app.a81f3c.js

Com -L, o curl mostra os blocos de headers dos redirects e da resposta final. Use o último bloco para avaliar o documento ou asset e registre url_effective para não atribuir os headers do primeiro 3xx ao recurso publicado. Para o HTML, Accept: text/html e Sec-Fetch-Dest: document exercitam a mesma transformação aplicada a uma navegação de navegador; sem esses headers, o teste pode observar validadores da resposta estática bruta que não chegam ao documento transformado.

Registre:

  • Cache-Control;
  • ETag e Last-Modified;
  • Age;
  • Vary;
  • CF-Cache-Status ou header equivalente, quando existir;
  • status e Content-Type;
  • URL final depois de redirects.

Teste a revalidação com o validador realmente recebido:

Terminal window
curl -sS -L -D - -o /dev/null \
-H 'Accept: text/html' \
-H 'Sec-Fetch-Dest: document' \
-w '\nURL final: %{url_effective}\n' \
-H 'If-None-Match: "valor-observado"' \
https://exemplo.com/

Não presuma que o resultado será 304: uma camada que transforma o HTML pode remover ou recalcular validadores. O objetivo é entender a resposta real, não forçar um status específico.

No navegador, diferencie:

  1. navegação normal em um perfil que já visitou o site;
  2. nova aba no mesmo perfil;
  3. perfil limpo;
  4. reload comum;
  5. reload ignorando cache, usado apenas como diagnóstico.

Se somente o último caso funciona, a política ou o versionamento ainda está errado.

Faça rollout e rollback de forma mensurável

Comece com uma rota e uma classe de assets. Publique, verifique headers, percorra jornadas e só depois amplie TTLs.

Antes de usar immutable por um ano, prove que:

  • a build gera nomes novos quando os bytes mudam;
  • o HTML novo referencia apenas os novos nomes;
  • o deploy envia o pacote completo;
  • assets removidos não são reutilizados por uma rota atual;
  • service worker, se houver, possui estratégia de atualização;
  • o time consegue identificar a versão pública.

Rollback de conteúdo também precisa de versão nova. Restaurar o código anterior e republicar o mesmo Site produz um HTML atual que aponta para os assets aprovados. Tentar sobrescrever um asset imutável na mesma URL mantém o defeito.

Prompt copiável para o agente

Revise o cache HTTP deste site sem publicar ainda.
Fonte e build: [caminhos]
URL atual: [URL]
Domínios: [lista]
- Inventarie HTML, assets versionados, URLs estáveis, APIs e respostas privadas.
- Registre Cache-Control, ETag, Last-Modified, Age, Vary, status e Content-Type.
- Não confunda no-cache com no-store.
- Proponha revalidação para HTML e TTL longo somente para assets com URL imutável.
- Não permita cache compartilhado para conteúdo personalizado.
- Confirme que mudanças de bytes geram novos nomes de asset.
- Teste navegação normal, perfil já visitado e perfil limpo.
- Entregue a matriz de políticas, evidências e rollback antes de publicar.

Checklist final

  • respostas foram classificadas por estabilidade e privacidade;
  • HTML com URL estável consegue descobrir a versão nova;
  • assets com TTL longo mudam de URL quando os bytes mudam;
  • no-cache e no-store foram usados com o significado correto;
  • respostas personalizadas não entram em cache compartilhado;
  • Vary contém somente dimensões necessárias;
  • validadores foram observados, não presumidos;
  • URL padrão e domínios próprios foram comparados;
  • reload ignorando cache não é a única prova;
  • service worker foi incluído no inventário;
  • rollout e rollback preservam uma versão coerente;
  • a evidência está ligada ao deploy publicado.

Cache bom não é o que guarda tudo pelo maior tempo. É o que reutiliza com segurança o que não mudou e encontra rapidamente a versão nova quando ela existe.