Cache é a principal alavanca para reduzir tempo de carregamento percebido e custo de rede em visitas repetidas. Em termos práticos, ele decide se o navegador (ou um intermediário, como CDN e proxy) pode reutilizar uma resposta já baixada, por quanto tempo, e como confirmar se ela ainda é válida. Uma estratégia bem definida combina três peças: diretivas de cache HTTP (Cache-Control/Expires), validação (ETag/Last-Modified) e versionamento de assets (hash no nome do arquivo ou query string). O objetivo é simples: permitir cache agressivo para arquivos estáticos versionados e cache controlado (com revalidação) para HTML e dados que mudam.
Como o cache HTTP funciona na prática
Quando o navegador solicita um recurso (HTML, CSS, JS, imagem, fonte), ele pode:
- Baixar do servidor (cache miss): primeira visita ou cache expirado sem validação.
- Reutilizar do cache sem falar com o servidor (fresh cache hit): o recurso ainda está “fresco” (fresh) segundo as regras de expiração.
- Revalidar com o servidor (stale + revalidation): o recurso está “stale” (expirado), mas o navegador pergunta se mudou. Se não mudou, recebe
304 Not Modifiede reaproveita o corpo já armazenado.
Esse comportamento é controlado principalmente por Cache-Control (HTTP/1.1) e, em menor grau, por Expires (legado). Para validação, entram ETag e Last-Modified.
Freshness: Cache-Control e Expires
Cache-Control define se pode cachear e por quanto tempo. Diretivas comuns:
max-age=seconds: tempo de vida em segundos a partir do momento da resposta.s-maxage=seconds: comomax-age, mas para caches compartilhados (CDN/proxy). Se presente, costuma prevalecer sobremax-ageem caches compartilhados.public: permite cache em caches compartilhados.private: permite cache apenas no navegador (não em CDN/proxy).no-store: não armazena em cache (nem navegador nem intermediários).no-cache: pode armazenar, mas deve revalidar antes de usar (nome confuso; não significa “não cachear”).must-revalidate: quando expirar, precisa revalidar; não pode servir stale sem validação.stale-while-revalidate=seconds: pode servir uma versão stale enquanto revalida em background (ótimo para reduzir latência percebida).stale-if-error=seconds: pode servir stale se o servidor falhar.
Expires é um timestamp absoluto. Em geral, prefira Cache-Control e use Expires apenas por compatibilidade com clientes antigos.
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
Validação: ETag e Last-Modified
Validação serve para evitar baixar novamente um arquivo que não mudou. O servidor envia um identificador de versão do conteúdo:
- ETag: um token (ex.: hash) representando a versão do recurso. O navegador guarda e, na próxima vez, envia
If-None-Match. - Last-Modified: data da última modificação. O navegador envia
If-Modified-Since.
Se nada mudou, o servidor responde 304 Not Modified sem corpo. Isso economiza banda, mas ainda há custo de RTT e de processamento no servidor. Por isso, para assets estáticos versionados, o ideal é evitar revalidação e usar cache longo (fresh hit).
ETag pode ser strong (mudou um byte, muda o ETag) ou weak (mudanças semânticas). Em geral, para assets estáticos, ETag strong é adequado. Para HTML dinâmico, ETag pode ser útil, mas cuidado com variações por usuário, cookies e personalização.
Princípio central: HTML revalida, assets versionados cacheiam “para sempre”
Uma abordagem robusta separa recursos em duas categorias:
- Documentos e respostas dinâmicas (HTML, JSON de APIs sensíveis a usuário): cache curto ou revalidação frequente. Ex.:
Cache-Control: no-cacheoumax-age=0, must-revalidate. - Assets estáticos versionados (CSS/JS/imagens/fontes com hash no nome): cache longo com
max-agealto eimmutable. Ex.:Cache-Control: public, max-age=31536000, immutable.
O motivo: se o nome do arquivo muda quando o conteúdo muda (ex.: app.3f2a1c.js), você pode manter cache agressivo sem risco de servir conteúdo antigo. A “invalidação” acontece pelo novo URL.
Passo a passo: desenhando uma política de cache
1) Inventarie tipos de recursos e como mudam
Liste os endpoints e arquivos que seu front-end entrega:
- HTML: muda com deploy? muda por usuário? muda por geografia/idioma?
- CSS/JS: gerados pelo build? podem ser versionados com hash?
- Imagens: são estáticas do deploy ou vêm de CMS? há transformação on-the-fly?
- Fontes: são estáticas? variam por subset?
- APIs: respostas são públicas ou dependem de autenticação?
Essa etapa define onde você pode ser agressivo (cache longo) e onde precisa ser conservador (revalidação).
2) Defina cache para HTML (documentos)
Para a maioria dos sites, o HTML deve permitir atualização rápida após deploy, mas ainda pode se beneficiar de revalidação (304). Uma configuração comum:
Cache-Control: no-cacheIsso permite armazenar, mas força revalidação a cada navegação. Alternativas:
- Cache curto para reduzir revalidações em navegação rápida:
Cache-Control: max-age=60, must-revalidate. - CDN com s-maxage para HTML público:
Cache-Control: public, max-age=0, s-maxage=60, must-revalidate(navegador revalida sempre, CDN pode segurar por 60s).
Se o HTML varia por idioma, device ou A/B test, use Vary corretamente (ex.: Vary: Accept-Encoding é comum; evite Vary: User-Agent se possível, pois fragmenta cache). Para conteúdo personalizado por cookie, normalmente use Cache-Control: private ou no-store dependendo da sensibilidade.
3) Defina cache para assets estáticos versionados
Para arquivos com hash no nome (ou outra forma de versionamento), aplique cache longo:
Cache-Control: public, max-age=31536000, immutableimmutable sinaliza que o recurso não mudará durante o período de cache, evitando revalidações desnecessárias. Isso reduz requisições e melhora visitas repetidas.
Importante: só use esse cache “anual” se você realmente versiona o arquivo. Se você servir /app.js sem hash e colocar max-age=31536000, você cria um cenário clássico de “usuário preso” em código antigo.
4) Configure validação (ETag/Last-Modified) onde faz sentido
Para HTML e endpoints que revalidam, habilite ETag e/ou Last-Modified. Exemplo de fluxo:
- Primeira resposta: servidor envia
ETag: "abc123". - Requisição seguinte: navegador envia
If-None-Match: "abc123". - Se não mudou: servidor responde
304.
Para assets com cache longo e immutable, ETag é menos relevante, porque o navegador não deve revalidar durante o período de freshness. Ainda assim, muitas infraestruturas enviam ETag por padrão; não é um problema, apenas não é o mecanismo principal.
5) Trate corretamente assets não versionados (ou gerados por CMS)
Nem tudo terá hash no nome. Imagens de CMS, por exemplo, podem manter o mesmo URL e mudar o conteúdo. Nesses casos:
- Use
Cache-Control: max-agemoderado (ex.: horas ou dias) e revalidação. - Garanta
ETagouLast-Modifiedconfiáveis. - Se você controla o CMS, prefira gerar URLs versionados (ex.: incluir um parâmetro de versão baseado em timestamp ou hash do arquivo).
Versionamento de assets: estratégias e trade-offs
Hash no nome do arquivo (cache busting por filename)
É a estratégia mais segura. O build gera nomes como:
/assets/app.3f2a1c.js/assets/styles.91b7d0.css
Quando o conteúdo muda, o hash muda, e o navegador baixa o novo arquivo. O antigo pode ficar no cache sem risco.
Requisitos para funcionar bem:
- O HTML (ou manifest) precisa apontar para os novos nomes a cada deploy.
- O servidor/CDN deve servir esses arquivos com cache longo e
immutable. - Evite reescrever o mesmo nome para conteúdo diferente.
Query string como versão (ex.: app.js?v=123)
Funciona, mas pode ser menos eficiente dependendo de CDNs/proxies e de regras de cache. Muitos ambientes modernos cacheiam bem com query string, mas nem todos. Se usar:
- Mantenha a versão estável e baseada em conteúdo (hash) ou build id.
- Garanta que a CDN está configurada para considerar query string no cache key quando apropriado.
Sem versionamento: revalidação obrigatória
Se você não consegue versionar, a alternativa é reduzir o TTL e/ou forçar revalidação:
Cache-Control: no-cacheIsso evita ficar preso em conteúdo antigo, mas aumenta RTTs e carga no servidor. Use apenas quando necessário.
ETag em ambientes com CDN e múltiplos servidores
Um cuidado comum: alguns servidores geram ETag baseado em metadados locais (inode, timestamp, tamanho). Em um cluster com múltiplas máquinas, o mesmo arquivo pode produzir ETags diferentes, causando revalidações inúteis (o cliente envia um ETag, outro servidor não reconhece e responde 200 com corpo).
Boas práticas:
- Prefira ETag baseado em hash do conteúdo (ou desabilite ETag para assets versionados e confie no cache por filename).
- Se você usa CDN, muitas vezes a CDN já faz validação e cache; alinhe a origem para não gerar inconsistência.
- Evite misturar ETag com compressão mal configurada: dependendo do servidor, o ETag pode variar entre versões gzip/brotli. O ideal é que a validação considere a representação correta (com
Vary: Accept-Encoding).
Headers essenciais e exemplos de configuração
Exemplo de política recomendada (visão geral)
- HTML público:
Cache-Control: no-cache(oumax-age=0, must-revalidate), comETag. - Assets com hash:
Cache-Control: public, max-age=31536000, immutable. - JSON público pouco volátil:
Cache-Control: public, max-age=60, stale-while-revalidate=300. - Respostas autenticadas:
Cache-Control: private, no-store(ouprivate, max-age=0, must-revalidatese puder armazenar localmente sem risco).
Nginx: cache longo para assets versionados e revalidação para HTML
server { location / { add_header Cache-Control "no-cache"; } location ~* \.(?:js|css|png|jpg|jpeg|gif|svg|webp|avif|woff2)$ { add_header Cache-Control "public, max-age=31536000, immutable"; try_files $uri =404; }}Se seus assets versionados ficam em um diretório específico (ex.: /assets/), prefira match por caminho em vez de extensão para reduzir risco de aplicar cache longo em arquivos não versionados.
Apache (.htaccess): exemplo equivalente
<IfModule mod_headers.c> <FilesMatch "\.(js|css|png|jpg|jpeg|gif|svg|webp|avif|woff2)$"> Header set Cache-Control "public, max-age=31536000, immutable" </FilesMatch> <FilesMatch "\.(html)$"> Header set Cache-Control "no-cache" </FilesMatch></IfModule>Adapte para sua estrutura real (por exemplo, aplicar cache longo apenas em /assets/).
CDN: s-maxage e stale-while-revalidate
Quando há CDN, você pode separar comportamento do navegador e do cache compartilhado:
Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=600Interpretação típica:
- Navegador sempre revalida (max-age=0).
- CDN pode servir por 5 minutos sem ir à origem (s-maxage=300).
- Após expirar, CDN pode servir stale enquanto revalida por até 10 minutos.
Passo a passo: validando se o cache está funcionando
1) Verifique headers no DevTools
No painel Network do DevTools, clique em um recurso e confira:
- Response Headers:
Cache-Control,ETag,Last-Modified,Age(quando vem de CDN),Vary. - Status: 200 (baixou), 304 (revalidou), ou “(from disk cache)/(from memory cache)” quando foi cache hit local.
Teste com “Disable cache” desmarcado (habilitado) e faça reload normal e hard reload para comparar.
2) Simule navegação repetida e deploy
Um teste prático:
- Carregue a página, observe que assets versionados retornam 200 na primeira vez.
- Recarregue: eles devem vir do cache (disk/memory) sem requisição, ou com requisição mínima dependendo do navegador.
- Faça um novo deploy que altere o conteúdo do JS/CSS e gere novos hashes.
- Recarregue: o HTML deve apontar para os novos arquivos, que serão baixados (200), enquanto os antigos permanecem no cache sem serem usados.
3) Cheque problemas comuns
- HTML com cache longo: usuários não recebem referência para novos assets após deploy. Sintoma: erros de chunk inexistente, JS quebrado, CSS antigo.
- Assets sem hash com cache longo: conteúdo antigo persistindo por dias/meses.
- Vary excessivo: baixa taxa de hit na CDN e mais requisições à origem.
- ETag inconsistente no cluster: revalidações que viram 200 desnecessariamente.
- Cache de respostas autenticadas: risco de vazamento de dados se marcado como
public.
Cache e segurança/privacidade: cuidados mínimos
Nem tudo deve ser cacheado. Regras práticas:
- Conteúdo com dados pessoais: prefira
Cache-Control: no-store. - Respostas que variam por autenticação/cookie: use
privatee evite CDN cachear, a menos que haja estratégia explícita (ex.: cache por token, geralmente não recomendado). - Se usar
Set-Cookieem uma resposta, pense duas vezes antes de permitir cache compartilhado.
Checklist de implementação (resumo operacional)
- Ative versionamento por hash para CSS/JS e, quando possível, para imagens/fontes estáticas.
- Sirva assets versionados com
Cache-Control: public, max-age=31536000, immutable. - Sirva HTML com revalidação (
no-cacheou TTL curto) e, se útil,ETag. - Use
s-maxageestale-while-revalidatepara tirar carga da origem via CDN. - Garanta que
Varynão fragmenta cache desnecessariamente. - Teste em DevTools: primeira visita (200), revisita (cache hit), revalidação (304) e pós-deploy (novos hashes).