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 Modifiede 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-cacheETag: "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.webpE a resposta pode declarar:
Cache-Control: public, max-age=31536000, immutableEsse 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-Cookiena resposta;Authorizationna 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-EncodingVary: Accept-LanguageNã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:
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.jsCom -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;ETageLast-Modified;Age;Vary;CF-Cache-Statusou header equivalente, quando existir;- status e
Content-Type; - URL final depois de redirects.
Teste a revalidação com o validador realmente recebido:
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:
- navegação normal em um perfil que já visitou o site;
- nova aba no mesmo perfil;
- perfil limpo;
- reload comum;
- 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-cacheeno-storeforam usados com o significado correto; - respostas personalizadas não entram em cache compartilhado;
-
Varyconté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.
