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
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.
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/2 200
content-type: text/css
cache-control: public, max-age=31536000, immutableSem 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 omax-ageneles, 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.publiceprivate:privatelimita ao cache do navegador do usuário;publiclibera caches compartilhados, inclusive para requisições comAuthorization.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-revalidateequivale na prática ano-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:
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:
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-cacheesperando bloquear o cache. Para isso éno-store. - Cachear resposta personalizada como
public. Uma CDN pode entregar os dados de um usuário a outro. Useprivate. - Misturar diretivas conflitantes.
private, no-cache, no-store, max-age=0, must-revalidateequivale ano-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
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.
Mais em Rede e domínios
Ver todos os 27 artigos- O que é load balancer (balanceamento de carga)Load balancer é o componente que distribui requisições entre vários servidores. Veja como funciona, os algoritmos, as camadas 4 e 7 e um exemplo com nginx.
- Registros DNS: A, CNAME, MX e TXT explicadosRegistros DNS são as instruções de uma zona que dizem para onde um nome aponta. Entenda A, AAAA, CNAME, MX, TXT, NS e CAA com exemplos e comandos dig.
- Como apontar um domínio para sua hospedagemPara apontar um domínio para sua hospedagem, crie um registro CNAME (ou A) no DNS do seu registrador. Veja o passo a passo, como testar e os erros comuns.
- O que é SSL/TLS e por que seu site precisaSSL/TLS é o protocolo que criptografa a conexão entre navegador e servidor e prova a identidade do site. Veja como funciona, o que é o certificado e quando usar.