Capa do Ebook gratuito Performance Front-End: Otimizando Core Web Vitals sem Mistério

Performance Front-End: Otimizando Core Web Vitals sem Mistério

Novo curso

19 páginas

Estratégias de cache e validação: HTTP caching, ETag e versionamento de assets

Capítulo 13

Tempo estimado de leitura: 11 minutos

+ Exercício

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 Modified e 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: como max-age, mas para caches compartilhados (CDN/proxy). Se presente, costuma prevalecer sobre max-age em 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...
Download App

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-cache ou max-age=0, must-revalidate.
  • Assets estáticos versionados (CSS/JS/imagens/fontes com hash no nome): cache longo com max-age alto e immutable. 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-cache

Isso 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, immutable

immutable 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-age moderado (ex.: horas ou dias) e revalidação.
  • Garanta ETag ou Last-Modified confiá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-cache

Isso 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 (ou max-age=0, must-revalidate), com ETag.
  • 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 (ou private, max-age=0, must-revalidate se 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=600

Interpretaçã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 private e evite CDN cachear, a menos que haja estratégia explícita (ex.: cache por token, geralmente não recomendado).
  • Se usar Set-Cookie em 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-cache ou TTL curto) e, se útil, ETag.
  • Use s-maxage e stale-while-revalidate para tirar carga da origem via CDN.
  • Garanta que Vary não fragmenta cache desnecessariamente.
  • Teste em DevTools: primeira visita (200), revisita (cache hit), revalidação (304) e pós-deploy (novos hashes).

Agora responda o exercício sobre o conteúdo:

Qual política de cache melhor equilibra atualização rápida do conteúdo e performance em visitas repetidas, considerando HTML e assets estáticos?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

A prática recomendada é HTML revalidar para refletir deploys rapidamente, enquanto assets com hash podem ter cache longo com immutable, pois a invalidação ocorre quando o URL muda.

Próximo capitúlo

Estratégias de carregamento e rede: preconnect, dns-prefetch e priorização

Arrow Right Icon
Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.