Cabeçalho Cache-Control: quanto tempo o navegador e a CDN guardam um arquivo

Cache-Control é o cabeçalho HTTP que diz a navegadores e CDNs se podem guardar uma resposta e por quantos segundos. Para arquivos com hash no nome, use public, max-age=31536000, immutable.

Atualizado em 6 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Entender o que max-age, no-cache, no-store e immutable fazem de fato
  • Escolher o Cache-Control certo para HTML, assets, imagens e respostas de API
  • Configurar o cabeçalho num servidor Node.js e verificar o resultado com curl
Nesta página

Toda vez que você abre um site, o navegador decide se baixa os arquivos de novo ou reaproveita uma cópia guardada. Quem manda nessa decisão é o servidor, por meio do cabeçalho Cache-Control. Configurado direito, ele deixa o site mais rápido e reduz o tráfego no servidor. Configurado errado, ele faz o visitante ver uma versão antiga por dias.

Se a CDN ainda é uma caixa-preta para você, leia antes o que é uma CDN.

frescavencidasubstituídaNavegadorCDNOrigemmax-agenão consultadaETag304200cópia nova
Fresca, a cópia sai do cache; vencida, o cache revalida pelo ETag e recebe 304 ou o arquivo novo com 200.

O que o Cache-Control faz?#

Cache-Control é um cabeçalho de resposta (e também de requisição) do HTTP. Ele traz uma lista de diretivas separadas por vírgula que valem para todos os caches no caminho: o do navegador (privado, de um único usuário) e os compartilhados, como CDNs e proxies reversos. As regras estão na RFC 9111.

http
HTTP/2 200
content-type: text/css
cache-control: public, max-age=31536000, immutable

Sem nenhuma informação de frescor, o cache pode estimar um prazo por conta própria (cache heurístico), em geral uma fração do tempo desde a última modificação. Por isso a regra é: declare o que você quer, não deixe a escolha para o cache.

Quais diretivas importam no dia a dia?#

  • max-age=N: a resposta é considerada fresca por N segundos, contados desde que o servidor a gerou. Fresca significa que o cache a entrega sem perguntar nada ao servidor.
  • s-maxage=N: o mesmo, mas só para caches compartilhados (CDN). Sobrescreve o max-age neles, e o navegador ignora.
  • no-cache: pode guardar, mas precisa revalidar com o servidor antes de cada reutilização. Não significa "não faça cache".
  • no-store: nenhum cache pode guardar a resposta. Use para dados sensíveis.
  • public e private: private limita ao cache do navegador do usuário; public libera caches compartilhados, inclusive para requisições com Authorization.
  • immutable: o conteúdo não muda enquanto estiver fresco, então o navegador nem revalida num recarregamento. Só faz sentido quando a URL muda junto com o conteúdo.
  • must-revalidate: depois de vencida, a resposta nunca é servida sem revalidar. max-age=0, must-revalidate equivale na prática a no-cache.
  • stale-while-revalidate=N: depois de vencida, o cache pode entregar a cópia velha por mais N segundos enquanto busca a nova em segundo plano.
  • stale-if-error=N: se o servidor responder com erro (500, 502, 503 ou 504), o cache pode entregar a cópia velha por até N segundos.

O erro mais comum é achar que no-cache impede o armazenamento. Quem impede é o no-store.

Como funciona a revalidação?#

Quando a cópia vence, o cache não precisa baixar o arquivo inteiro de novo. Ele pergunta ao servidor se algo mudou, enviando If-None-Match com o ETag que recebeu (ou If-Modified-Since com o Last-Modified). Se nada mudou, o servidor responde 304 Not Modified, sem corpo, e a cópia volta a ser fresca. Se mudou, responde 200 com o arquivo novo.

Esse é o mecanismo por trás do no-cache: o navegador sempre faz uma ida ao servidor, mas, na maioria das vezes, recebe só um 304 de poucos bytes.

Qual Cache-Control usar para cada tipo de arquivo?#

Tipo de resposta Cabeçalho Por quê
JS, CSS e imagens com hash no nome (app.3f9a1c.js) public, max-age=31536000, immutable A URL muda quando o conteúdo muda, então pode ficar um ano
index.html e páginas que apontam para os assets no-cache Sempre revalida e passa a apontar para os arquivos novos
Imagem ou arquivo sem hash no nome public, max-age=86400 (ou menos) Aceita ficar velho por um tempo conhecido
Resposta de API personalizada por usuário private, no-cache Nunca vai para cache compartilhado
Dado sensível (token, extrato) no-store Não pode ficar guardado em lugar nenhum
Conteúdo público que pode atrasar alguns minutos max-age=60, stale-while-revalidate=300 Resposta instantânea e atualização em segundo plano
TTL diferente na CDN e no navegador max-age=60, s-maxage=3600 A CDN guarda por 1 hora, o navegador por 1 minuto

A lógica das duas primeiras linhas é a mais importante: o HTML nunca fica muito tempo em cache e os arquivos versionados ficam por um ano. Cada deploy gera nomes novos, o HTML novo aponta para eles e ninguém vê versão velha. Geradores como o Vite já põem o hash no nome. Veja mais em o que é um site estático.

Como configurar no Node.js (Express)?#

O express.static aceita as opções maxAge e immutable. Com setHeaders você troca o cabeçalho de acordo com o arquivo. Exemplo com Express 5:

js
import express from "express";

const app = express();

// Arquivos com hash no nome: um ano, imutáveis.
app.use(
  "/assets",
  express.static("dist/assets", { maxAge: "1y", immutable: true }),
);

// O restante (index.html etc.): sempre revalida.
app.use(
  express.static("dist", {
    setHeaders(res) {
      res.setHeader("Cache-Control", "no-cache");
    },
  }),
);

// API com dados do usuário: nunca em cache compartilhado.
app.get("/api/me", (req, res) => {
  res.set("Cache-Control", "private, no-cache");
  res.json({ name: "Ana" });
});

app.listen(process.env.PORT ?? 3000);

O primeiro app.use responde com Cache-Control: public, max-age=31536000, immutable. Em outros servidores a ideia é a mesma: no Nginx, use add_header Cache-Control "public, max-age=31536000, immutable"; dentro do location /assets/.

Como verificar que funcionou?#

Peça só os cabeçalhos com curl -I e confira o cache-control:

bash
curl -sI https://seu-dominio.com/assets/app.3f9a1c.js | grep -i -E "cache-control|^age|etag"

Se há uma CDN no caminho, um cabeçalho age com valor maior que zero indica que a resposta veio do cache dela. No navegador, a aba Rede (Network) das ferramentas de desenvolvedor mostra "(disk cache)" ou "(memory cache)" quando a cópia local foi reutilizada. Atenção: recarregar com F5 ou Ctrl+Shift+R envia max-age=0 ou no-cache na requisição, então teste abrindo a URL numa aba nova.

Quais erros mais atrapalham?#

  • Max-age longo em arquivo sem hash. O visitante fica com o CSS velho até vencer. Só use prazo longo com URL versionada.
  • Max-age longo no HTML. Mesmo com assets novos, o navegador não busca o HTML que aponta para eles.
  • Usar no-cache esperando bloquear o cache. Para isso é no-store.
  • Cachear resposta personalizada como public. Uma CDN pode entregar os dados de um usuário a outro. Use private.
  • Misturar diretivas conflitantes. private, no-cache, no-store, max-age=0, must-revalidate equivale a no-store; escolha uma intenção.
  • Esquecer o cache da CDN depois de corrigir. Mudar o cabeçalho não limpa o que já foi guardado; é preciso fazer um purge ou esperar vencer.

Como fazer isso na Square Cloud#

Na Square Cloud, todo plano inclui uma CDN global que respeita o Cache-Control enviado pela sua aplicação. A receita deste guia vale sem ajuste: hash no nome dos assets com max-age de um ano, no-cache no HTML e private ou no-store no que pertence a um usuário. Você define os cabeçalhos no código, como no exemplo com Express, faz o deploy e confere com curl -I no subdomínio *.squareweb.app que toda aplicação web recebe com HTTPS.

Quando um arquivo errado já ficou guardado, use o purge de cache, disponível no subdomínio squareweb.app e no domínio personalizado (a partir do plano Standard), pelo painel ou pela extensão do VS Code. Ele apaga o cache do host inteiro, então trate-o como correção pontual e deixe as URLs versionadas cuidarem dos deploys do dia a dia.

Para arquivos enviados pelos usuários, o Blob Storage permite definir o Cache-Control por objeto e regras de cache por pasta, sem passar pela sua aplicação. Veja o purge em domínios personalizados e a CDN em rede.

Rede

Domínio próprio, TLS automático e CDN global para o seu app

Saiba mais

Perguntas frequentes

Qual a diferença entre no-cache e no-store?

no-cache permite guardar a resposta, mas obriga a revalidar com o servidor antes de reutilizá-la. no-store proíbe qualquer cache de guardar a resposta. Para dados sensíveis, use no-store.

Qual o valor máximo de max-age?

Não há um limite prático que você precise decorar: 31536000 segundos (um ano) é o valor usual para arquivos versionados. Valores negativos ou decimais têm comportamento indefinido.

O que é s-maxage?

É o tempo de frescor válido só para caches compartilhados, como CDNs e proxies. Ele sobrescreve o max-age nesses caches, e o navegador o ignora.

Por que o navegador não pega a versão nova do meu arquivo?

Quase sempre o arquivo foi servido com um max-age longo e a URL não mudou. Renomeie o arquivo com um hash do conteúdo ou reduza o max-age de quem muda com frequência, como o HTML.

Próximo artigoDomínio personalizado: como usar seu próprio domínio no appPara usar um domínio personalizado, adicione o domínio na hospedagem e crie no DNS os registros que ela informar. Veja o passo a passo, www, HTTPS e erros.

Mais em Rede e domínios

Ver todos os 27 artigos