Anúncios incomodam? Ir Sem anúncios Hoje

Cabeçalhos HTTP Cache-Control O que realmente significam no-cache, no-store e max-age

Atualizado em

Uma explicação prática dos diretivas Cache-Control — o que os navegadores e CDNs realmente fazem com no-cache, no-store, max-age, s-maxage e ETags. Incluindo os erros que mais desenvolvedores enfrentam em produção.

Cabeçalhos HTTP Cache-Control: O que significam realmente no-cache, no-store e max-age 1
ANUNCIADO Remover?

Provavelmente já escreveu Cache-Control: no-cache e assumiu que o navegador ignoraria completamente a cache. Isso não acontece. no-cache significa "revalidar antes de servir da cache". Se você realmente deseja que a resposta nunca seja armazenada, isso é no-store. Essa confusão causa bugs com dados obsoletos em produção, que são difíceis de diagnosticar porque tudo parece normal na aba Network na primeira carga.

Vamos analisar cada diretiva claramente — o que os navegadores fazem com ela, o que os CDNs fazem com ela, e os erros que surgem ao misturá-las.

Referência completa das diretivas Cache-Control

DiretivaComportamento do navegadorComportamento do CDN / proxyUso típico
max-age=NCache por N segundosCache por N segundos (a menos que s-maxage sobreponha)Ativos estáticos, respostas de API
s-maxage=NIgnoradoCache por N segundosTTL do CDN separado do navegador
no-cacheCache, mas revalida em cada requisiçãoCache, mas revalida em cada requisiçãoConteúdo que muda frequentemente com ETags
no-storeNão armazene em nenhum lugarNão armazene em nenhum lugarRespostas de autenticação, dados sensíveis do usuário
must-revalidateSem entrega de dados obsoletos — revalide ou falheSem entrega de dados obsoletos — revalide ou falheRespostas de API onde dados obsoletos são quebrantes
proxy-revalidateIgnoradoSem entrega de dados obsoletos em caches compartilhadosObrigatório revalidação no CDN específico
privateO navegador pode armazenarNão deve ser armazenadoPáginas específicas do usuário
publicQualquer cache pode armazenarPode ser armazenadoRecursos estáticos compartilhados
immutableNunca revalida dentro do max-ageVaria conforme o CDNAtivos hashados / versionados
stale-while-revalidate=NServe dados obsoletos por N segundos enquanto obtém a versão atualVaria conforme o CDNVelocidade sem rigidez de dados obsoletos

max-age e s-maxage: TTL do navegador vs TTL do CDN

max-age=N informa tanto o navegador quanto os CDNs quanto segundos a resposta é fresca. Após N segundos, a resposta armazenada se torna obsoleta e deve ser revalidada antes de ser usada.

s-maxage=N é exclusiva do CDN. Os navegadores a ignoram completamente. Se você deseja que o CDN armazene por uma hora mas o navegador apenas por 5 minutos:

Cache-Control: max-age=300, s-maxage=3600

O navegador armazena por 5 minutos. CloudFront, Fastly, Nginx — eles usam 3600 segundos. Uma armadilha comum: definir max-age=0 achando que desativa a cache. Isso não acontece. max-age=0 significa que a resposta se torna imediatamente obsoleta — o navegador ainda a armazena e revalida, provavelmente obtendo um 304. Se você nunca quiser que ela seja armazenada, use no-store.

no-cache: O que você realmente pensa

Cache-Control: no-cache não significa "não use a cache". Significa "não sirva da cache sem revalidar com o servidor primeiro".

A sequência quando um navegador tem uma resposta armazenada com no-cache:

  1. Um novo pedido chega para o recurso armazenado
  2. O navegador envia o ETag da resposta armazenada (via If-None-Match) ou o timestamp de modificação para o servidor
  3. Se o conteúdo não mudou → o servidor retorna 304 Not Modified sem corpo
  4. Se o conteúdo mudou → o servidor retorna 200 OK com a nova resposta

A vantagem em relação a no-store: você ainda obtém as economias de banda das respostas 304. Se seu conteúdo muda ocasionalmente, mas precisa ser fresco quando muda, no-cache combinado com um ETag é a combinação correta.

no-store: O verdadeiro "não armazene isso"

Cache-Control: no-store significa que a resposta não deve ser armazenada em nenhum lugar — nem no cache do navegador, nem no CDN, nem em um proxy intermediário. Nenhuma cópia, ponto final.

Use isso para:

  • Respostas de autenticação (tokens de login, dados de sessão)
  • Dados pessoais sensíveis
  • Conteúdo de uso único (confirmações de pagamento, páginas de OTP)

Um detalhe subtis: no-store não impede que uma página apareça no cache de navegação do navegador (bfcache). Os navegadores mantêm uma cópia em memória para melhorar o desempenho de navegação, separada do cache HTTP. Se você precisa lidar com problemas do botão voltar após logout, integre o evento pageshow e verifique event.persisted.

must-revalidate: Sem graça de dados obsoletos

Os specs de cache HTTP permitem que os caches sirvam respostas obsoletas quando o servidor original não está acessível — uma característica de resiliência que a maioria dos desenvolvedores não conhece. must-revalidate remove essa margem: uma vez que uma resposta armazenada se torna obsoleta, o cache deve revalidar ou retornar um 504. Sem entrega de dados obsoletos em qualquer circunstância.

# Without must-revalidate: CDN may serve stale if origin is slow or down
Cache-Control: max-age=3600

# With must-revalidate: stale = error, not a fallback
Cache-Control: max-age=3600, must-revalidate

Use isso para respostas de API onde a entrega de dados obsoletos quebra a funcionalidade — contagens de estoque, preços, estado de autenticação — e não apenas fazem parecer um pouco errado.

private vs public: O erro do CDN que revela dados do usuário

private significa que a resposta é destinada a um usuário específico. Os navegadores podem armazená-la; os caches compartilhados (CDNs, proxies reversos) não devem.

public permite que qualquer cache — incluindo os compartilhados — armazene a resposta. Alguns caches só armazenam respostas de requisições autenticadas se você marcar explicitamente public.

O erro real no mundo real: um desenvolvedor copia e cola Cache-Control: public, max-age=3600 de um ativo estático para uma página que inclui dados específicos do usuário. O CDN armazena a resposta. O usuário B faz o mesmo pedido e recebe a página do usuário A do cache. Isso não é teórico — o GitHub teve uma variante disso em 2018. Marque respostas autenticadas ou específicas do usuário private explicitamente, mesmo que você pense que seu CDN "sabe" que não deve armazená-las.

ETags e requisições condicionais

ETags são como o servidor diz "aqui está um rastro dessa resposta". O navegador armazena o ETag junto com a resposta armazenada e envia-o de volta na próxima requisição via If-None-Match. Se o conteúdo não mudou, o servidor retorna 304 Not Modified sem corpo — mesma enquadramento de frescor que no-cache, com muito menos uso de banda.

O no-cache + Fluxo de ETag:

→ GET /api/config HTTP/1.1

← HTTP/1.1 200 OK
   Cache-Control: no-cache
   ETag: "abc123"
   [full response body]

→ GET /api/config HTTP/1.1
   If-None-Match: "abc123"

← HTTP/1.1 304 Not Modified
   [no body — browser uses its cached copy]

Dois tipos de ETag:

  • ETags fortes ("abc123") — identicamente byte a byte. Obrigatório para suporte de requisições de intervalo no CDN.
  • ETags fracos (W/"abc123") — semanticamente equivalentes, mas não necessariamente identicamente byte a byte. Adequado para revalidação no navegador, não para requisições de intervalo.

O Nginx gera ETags automaticamente com base no tempo de modificação e no tamanho do arquivo. O Express não os adiciona por padrão — use app.set('etag', 'strong') ou o etag middleware explicitamente.

Last-Modified e If-Modified-Since

Mesmo conceito que ETags, mas mais grosseiro — baseado em timestamp em vez de hash de conteúdo. O servidor inclui Last-Modified; o navegador envia If-Modified-Since em requisições subsequentes.

O problema: se você redeploys e os tempos de modificação dos arquivos atualizam mesmo que o conteúdo não tenha mudado, os caches invalidam desnecessariamente. Um hash de conteúdo baseado em ETag não terá esse problema. Use ETags sempre que possível e trate Last-Modified como um fallback para servidores que não suportam ETags.

Vary: O cabeçalho que multiplica silenciosamente seu cache

O Vary cabeçalho informa aos caches que a resposta pode diferir com base em outros cabeçalhos de requisição. Cada combinação única desses cabeçalhos recebe sua própria entrada de cache.

Vary: Accept-Encoding

Isso informa aos caches para armazenar respostas separadas para gzip, brotli e codificação identidade. Correto e comum. O perigoso: Vary: Cookie. Cada usuário tem um cookie único definido, então cada usuário recebe sua própria entrada de cache — efetivamente desativando o cache compartilhado. Muitas frameworks adicionam Vary: Cookie silenciosamente. Se a taxa de acerto do seu cache do CDN for inexplicavelmente baixa, apesar de valores generosos de max-age , verifique seus cabeçalhos de resposta por Vary: Cookie que estão sendo inseridos do middleware de sessão.

Vary: * significa "não armazene isso em absoluto" na prática — cada requisição é tratada como única. É equivalente a no-store para CDNs.

Forçando atualização com parâmetros de consulta

Quando você precisa forçar downloads frescos de ativos versados, anexar um parâmetro de consulta é a abordagem padrão — a string de consulta faz parte da URL, então é tratada como um novo recurso tanto pelo navegador quanto pelos CDNs:

/app.js?v=2.1.4
/styles.css?hash=a1b2c3d4e5f6

Se você estiver construindo parâmetros de forçamento de cache dinamicamente a partir de hashes de conteúdo ou strings de versão que podem conter caracteres especiais, certifique-se de codificar porcentualmente antes de anexar. O Codificador/Decodificador de URL trata isso rapidamente se você estiver testando ou construindo URLs manualmente.

Três erros que afetam a maioria dos desenvolvedores

1. Usar no-cache quando quer no-store. Se você está lidando com respostas de autenticação, endpoints de logout ou qualquer coisa com dados pessoais sensíveis (PII), você quer no-store. no-cache deixa os dados no cache do navegador (apenas marcados como obsoletos); no-store remove completamente o rastro. A diferença importa quando os usuários compartilham dispositivos.

2. Não definir s-maxage para controle do CDN. Baixar como .yml s-maxage, seu CDN usa max-age. Se max-age é curto para a frescor do navegador (por exemplo, 60 segundos), seu CDN armazena por 60 segundos também — o que provavelmente não é o que você deseja. Separe explicitamente os dois TTLs.

3. usar public em endpoints que retornam dados do usuário. Esse é o incidente de segurança, não apenas um erro de desempenho. Qualquer resposta personalizada ou autenticada deve ser private. Default para private e opte por public apenas para recursos realmente compartilhados.

Montando Tudo

O modelo mental: no-cache é sobre a enquadramento de frescor — a resposta vive no cache, mas precisa de uma aprovação do servidor antes de ser usada. no-store é sobre deixar nenhuma rastro. max-age é o TTL do seu navegador. s-maxage é o TTL separado do seu CDN. ETags são o que tornam a revalidação barata.

Obtenha a private/public diferença certa em qualquer endpoint que toca dados do usuário. Esse único erro — copiar um cabeçalho de cache de um ativo estático para um endpoint autenticado — é o que se transforma em vazamento de dados entre usuários quando o seu CDN começa a armazenar respostas personalizadas.

Quer eliminar anúncios? Fique sem anúncios hoje mesmo

Instale nossas extensões

Adicione ferramentas de IO ao seu navegador favorito para acesso instantâneo e pesquisa mais rápida

Ao Extensão do Chrome Ao Extensão de Borda Ao Extensão Firefox Ao Extensão Opera

O placar chegou!

Placar é uma forma divertida de acompanhar seus jogos, todos os dados são armazenados em seu navegador. Mais recursos serão lançados em breve!

ANUNCIADO Remover?
ANUNCIADO Remover?
ANUNCIADO Remover?

Notícias com destaques técnicos

Envolver-se

Ajude-nos a continuar fornecendo ferramentas gratuitas valiosas

Compre-me um café
ANUNCIADO Remover?