Cache HTTP: cache forte, negociado e estratégias de CDN
Cache HTTP: a primeira linha de defesa da otimização de performance
Uma configuração de cache adequada pode alcançar zero requisições de rede no segundo acesso, reduzindo o tempo de carregamento de segundos para milissegundos.
| Tipo de cache | Mecanismo | Requisita o servidor? | Velocidade |
|---|---|---|---|
| Cache forte | Cache-Control / Expires | Não | Mais rápida (0ms) |
| Cache negociado | ETag / Last-Modified | Sim (requisição condicional) | Rápida (resposta 304) |
| Sem cache | no-cache / no-store | Sim (requisição completa) | Mais lenta |
I. Cache forte
Diretivas Cache-Control
Cache-Control: max-age=31536000, immutable
| Diretiva | Função | Cenário aplicável |
|---|---|---|
max-age=N |
Cachear por N segundos | Recursos estáticos |
s-maxage=N |
CDN cacheia por N segundos (sobrescreve max-age) | Cenários CDN |
immutable |
Não revalidar mesmo se expirado | Nomes de arquivo com hash |
public |
Permitir cache intermediário | CDN |
private |
Apenas cache do navegador | Conteúdo personalizado do usuário |
no-cache |
Deve verificar antes de cada uso | Páginas HTML |
no-store |
Não cachear nada | Dados sensíveis |
must-revalidate |
Deve verificar após expirar | Respostas de API |
stale-while-revalidate=N |
Retornar valor antigo após expirar, atualizar em segundo plano | Atualização progressiva |
Expires (padrão antigo)
Expires: Thu, 02 Jun 2027 00:00:00 GMT
Prioridade: Cache-Control: max-age > Expires.
II. Cache negociado
ETag / If-None-Match
# Primeira requisição
GET /api/data
→ 200 OK
ETag: "abc123"
Cache-Control: max-age=0
# Requisição seguinte (condicional)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified (sem corpo de resposta, economiza largura de banda)
Last-Modified / If-Modified-Since
# Primeira requisição
GET /style.css
→ 200 OK
Last-Modified: Mon, 01 Jun 2026 12:00:00 GMT
# Requisição seguinte
GET /style.css
If-Modified-Since: Mon, 01 Jun 2026 12:00:00 GMT
→ 304 Not Modified
ETag vs Last-Modified
| Dimensão | ETag | Last-Modified |
|---|---|---|
| Precisão | Nível de byte | Nível de segundo |
| Performance | Requer cálculo de hash | Leitura direta do tempo do arquivo |
| Confiabilidade | Alta | Baixa (múltiplas modificações em 1 segundo são perdidas) |
| Distribuído | Deve garantir hash consistente | Naturalmente consistente |
Recomendação: Priorizar ETag.
III. Matriz de estratégias de cache
Páginas HTML
Cache-Control: no-cache
HTML é o arquivo de entrada, deve ser verificado a cada vez para garantir que os usuários recebam a versão mais recente.
Recursos estáticos com Hash
Cache-Control: max-age=31536000, immutable
O nome do arquivo contém Hash (ex., app.abc123.js), quando o conteúdo muda, o Hash muda → a URL muda → nova requisição. A URL antiga nunca muda, pode ser cacheada por um ano e marcada como immutable.
Recursos estáticos sem Hash
Cache-Control: max-age=86400, stale-while-revalidate=604800
Após expirar, primeiro retorna o valor antigo, atualiza silenciosamente em segundo plano, imperceptível para o usuário.
Respostas de API
Cache-Control: private, max-age=0, must-revalidate
Respostas de API geralmente contêm dados do usuário, private proíbe cache CDN, must-revalidate garante que valores antigos não sejam retornados após expirar.
IV. Cabeçalho Vary: extensão da chave de cache
Vary: Accept-Encoding, Accept-Language
Vary diz ao cache: além da URL, deve distinguir versões de cache pelos valores destes cabeçalhos de requisição.
| Valor Vary | Significado | Cenário |
|---|---|---|
Accept-Encoding |
Cachear por método de compressão | gzip vs br |
Accept-Language |
Cachear por idioma | Sites multilíngues |
Cookie |
Cachear por Cookie | Páginas personalizadas |
* |
Cada requisição com cache independente | Equivalente a não cachear |
Nota: Vary: Cookie causa taxa de acerto de cache muito baixa, usar com cautela.
V. Estratégia de cache CDN
Controle de busca de origem
# CDN cacheia 1 dia, navegador cacheia 5 minutos
Cache-Control: public, s-maxage=86400, max-age=300
| Cenário | s-maxage | max-age | Descrição |
|---|---|---|---|
| Recursos estáticos | 31536000 | 31536000 | CDN e navegador com cache longo |
| Respostas de API | 300 | 0 | CDN cacheia 5 minutos, navegador não cacheia |
| HTML | 60 | 0 | CDN cacheia 1 minuto, navegador não cacheia |
| Conteúdo personalizado | — | 0 | CDN não cacheia, navegador não cacheia |
Atualização de cache CDN
| Método | Velocidade | Aplicável |
|---|---|---|
| Pré-aquecimento de URL | Rápido | Novo conteúdo publicado |
| Atualização completa | Lento | Correção urgente |
| Atualização de diretório | Médio | Atualização em lote |
| Cache-Control: no-cache | Automático | Cenários de API |
Stale-While-Revalidate na prática
Cache-Control: max-age=60, stale-while-revalidate=86400
Segundo 0: Requisição → Cache MISS → Buscar origem → Retornar valor fresco
Segundo 30: Requisição → Cache HIT → Retornar valor em cache
Segundo 90: Requisição → Cache STALE → Retornar valor antigo imediatamente + buscar origem em segundo plano
Segundo 91: Busca de origem concluída → Cache atualizado
Segundo 120: Requisição → Cache HIT → Retornar valor novo
Efeito: O usuário nunca espera a busca de origem, atualização silenciosa em segundo plano.
VI. Estratégia de invalidação de cache
Hash de conteúdo (recomendado)
<!-- Hash gerado em tempo de compilação -->
<script src="/app.abc123.js"></script>
<link href="/style.def456.css">
// Configuração webpack
output: {
filename: '[name].[contenthash:8].js',
}
Parâmetro de consulta de versão
<!-- Simples mas CDN pode não cachear parâmetros ?v= -->
<script src="/app.js?v=2.0.0"></script>
Problema: Alguns CDNs ignoram parâmetros de consulta, fazendo com que o cache não seja atualizado.
Timestamp (não recomendado)
<!-- Muda toda vez, destrói completamente o cache -->
<script src="/app.js?t=1717315200"></script>
VII. Cache de Service Worker
Estratégia Cache First
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request))
);
});
Estratégia Stale While Revalidate
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open('dynamic').then(cache =>
cache.match(event.request).then(cached => {
const fetchPromise = fetch(event.request)
.then(response => {
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
})
)
);
});
Seleção de estratégia de cache
| Estratégia | Origem prioritária | Aplicável |
|---|---|---|
| Cache First | Cache | Fontes, imagens |
| Network First | Rede | API, HTML |
| Stale While Revalidate | Cache + atualização em segundo plano | CSS, JS |
| Network Only | Rede | Requisições não GET |
| Cache Only | Cache | Páginas offline |
VIII. Depuração de cache
Chrome DevTools
- DevTools → Painel Network
- Ver coluna
Size:from disk cache: Acerto no cache de discofrom memory cache: Acerto no cache de memória- Tamanho real: Sem acerto
- Ver cabeçalho de resposta
Age: Tempo que está em cache - Marcar Disable cache: Desabilitar cache (quando DevTools está aberto)
Verificação com curl
# Ver cabeçalhos de resposta
curl -I https://example.com/style.css
# Enviar requisição condicional
curl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# 304 indica cache negociado acertado
# Simular diferente Accept-Encoding
curl -I -H 'Accept-Encoding: br' https://example.com/style.css
IX. Lista de melhores práticas
- HTML: no-cache, garantir que a entrada sempre seja a mais recente
- JS/CSS com Hash: max-age=1 ano + immutable
- API: must-revalidate, deve verificar após expirar
- CDN: s-maxage para controlar a frequência de busca de origem
- Vary: Accept-Encoding, distinguir métodos de compressão
- Usar ETag em vez de Last-Modified
- stale-while-revalidate para atualização imperceptível
- Service Worker para complementar capacidades offline