A gestão eficaz do cache é crucial para a performance de websites e aplicações. O cabeçalho HTTP Cache-Control é a ferramenta principal para instruri navegadores e proxies sobre como e por quanto tempo os recursos devem ser armazenados em cache. Este guia detalha as diretivas do Cache-Control e como configurá-las no Nginx para otimizar a experiência do utilizador.
Entendendo o Cache-Control
Cache-Control substituiu o cabeçalho Expires do HTTP/1.0 e oferece um controle mais granular sobre o comportamento do cache. As suas diretivas podem ser agrupadas em:
Diretivas de Armazenamento (Quem pode cachear)
public: Permite que qualquer cache (incluindo CDNs e proxies) armazene a resposta.private: Indica que a resposta é específica para um utilizador e só deve ser armazenada no cache do navegador do utilizador.no-cache: Permite o armazenamento em cache, mas força o cliente a revalidar a cópia em cache com o servidor antes de a usar.no-store: A diretiva mais restritiva, impede que qualquer cache armazene a resposta.
Diretivas de Tempo (Por quanto tempo cachear)
max-age=<segundos>: Define o tempo máximo de vida do recurso em cache, em segundos, a partir do momento da requisição.s-maxage=<segundos>: Similar aomax-age, mas aplica-se a caches partilhados (proxies, CDNs).Expires: <data>: Cabeçalho HTTP/1.0 mais antigo, define uma data e hora de expiração absoluta.
Diretivas de Revalidação e Validação
must-revalidate: O cache deve verificar com o servidor se o recurso ainda é válido após a expiração.proxy-revalidate: Similar aomust-revalidate, mas aplica-se apenas a caches partilhados.immutable: Indica que o recurso nunca mudará durante a sua URL. Útil para recursos com nomes de ficheiro baseados em hash.
Configuração de Cache no Nginx
O Nginx permite definir cabeçalhos Cache-Control para diferentes localizações (blocos location) no seu ficheiro de configuração.
Exemplos de Configuração
Recursos Estáticos de Longo Prazo (Imagens, CSS, JS com hash)
Para recursos que raramente mudam e são referenciados por um nome de ficheiro que inclui um hash (gerado por ferramentas como Webpack ou Vite), podemos definir um cache de longa duração:
location ~* \.(js|css|png|jpg|jpeg|gif|ico|webp|svg|mjs|wasm)$ {
# Cache por um ano (31536000 segundos)
# immutable: o conteúdo não mudará para esta URL
add_header Cache-Control "public, immutable, max-age=31536000";
access_log off; # Opcional: não registrar logs para estes ficheiros
expires 1y; # Diretiva curta para compatibilidade com alguns caches mais antigos
}
Recursos de Conteúdo Dinâmico ou Personalizado
Para APIs ou páginas que podem conter dados específicos do utilizador, o cache deve ser mais restrito:
location /api/ {
# Apenas o navegador do utilizador pode cachear, por 5 minutos
add_header Cache-Control "private, max-age=300";
}
location /user/profile {
# Cache curto, mas revalidar sempre após expirar
add_header Cache-Control "private, max-age=60, must-revalidate";
}
Páginas HTML
Páginas HTML frequentemente contêm conteúdo que precisa ser atualizado. Um cache agressivo pode levar à exibição de conteúdo desatualizado.
location ~* \.html$ {
# Não cachear ou cachear por um tempo muito curto e revalidar
add_header Cache-Control "no-cache, max-age=0";
# Pragma é um cabeçalho HTTP/1.0 legado, mas pode ajudar com caches mais antigos
add_header Pragma "no-cache";
add_header Expires "0";
}
Dados Sensíveis
Informações sensíveis nunca devem ser cacheado.
location /secure/data {
# A diretiva mais restritiva
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
Mecanismos de Validação de Cache
Quando o Cache-Control não impede a validação (como com no-cache ou após o max-age expirar), o servidor pode usar cabeçalhos de validação para informar ao cliente se o recurso em cache ainda é válido:
- ETag (Entity Tag): Um identificador único para uma versão específica de um recurso. O cliente envia o
ETagno cabeçalhoIf-None-Match. Se oETagcorresponder, o servidor responde com304 Not Modified. - Last-Modified: A data e hora em que o recurso foi modificado pela última vez. O cliente envia esta data no cabeçalho
If-Modified-Since. Se o recurso não foi modificado desde essa data, o servidor responde com304 Not Modified.
O Nginx pode ser configurado para gerar ou verificar estes cabeçalhos:
location ~* \.(js|css)$ {
# ETag é geralmente ativado por padrão se o módulo http_etag estiver habilitado
etag on;
# Last-Modified é ativado por padrão para ficheiros estáticos
# O Nginx usa o tempo de modificação do ficheiro
expires 1y;
add_header Cache-Control "public, immutable";
}
Estratégias Avançadas e Boas Práticas
Cacheamento por Camadas
- Recursos versionados (com hash): Cache permanente com
immutableemax-agelongo (ex: 1 ano). - Imagens e mídia: Cache de longa duração (ex: 30 dias ou 1 ano), permitindo revalidação.
- HTML: Sem cache ou cache muito curto com revalidação obrigatória.
- APIs dinâmicas: Cache privado com
max-agecurto emust-revalidate.
Diretivas Modernas
stale-while-revalidate e stale-if-error podem melhorar a experiência do utilizador em casos de falha de rede ou revalidação:
location ~* \.(js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable, stale-while-revalidate=86400";
add_header Cache-Control "stale-if-error=604800";
}
stale-while-revalidate=86400: Permite que o cache seja usado por até 1 dia após a expiração, enquanto uma nova cópia é buscada em segundo plano.
stale-if-error=604800: Se ocorrer um erro ao tentar revalidar o cache, permite usar a cópia em cache por até 1 semana.
Debug e Moniotramento de Cache
Adicionar cabeçalhos personalizados no Nginx pode ajudar a depurar o comportamento do cache:
location / {
# Exibe o status do cache do proxy (se proxy_cache estiver ativo)
add_header X-Cache-Status $upstream_cache_status;
# Exibe o cabeçalho Cache-Control enviado na resposta
add_header X-Sent-Cache-Control $sent_http_cache_control;
# Exibe o ETag enviado na resposta
add_header X-Sent-ETag $sent_http_etag;
# Outras informações úteis
add_header X-Request-URI $request_uri;
add_header X-File-Path $request_filename;
}
Variáveis comuns para depuração:
$upstream_cache_status: Indica se um recurso foiHIT,MISS,EXPIRED, etc. (válido apenas comproxy_cache).$sent_http_cache_control: O valor do cabeçalhoCache-Controlenviado na resposta.$sent_http_etag: O valor do cabeçalhoETagenviado na resposta.$sent_http_last_modified: O valor do cabeçalhoLast-Modifiedenviado na resposta.
Verificação do Cache no Navegador
Utilize as Ferramentas do Desenvolvedor do navegador (geralmente F12):
- Abra a aba "Network".
- Selecione o recurso que deseja inspecionar.
- Verifique os "Response Headers" para
Cache-Control,ETag,Expires,Last-Modified. - Observe a coluna "Size":
(memory cache)ou(disk cache): Cache forte atingido, sem requisição de rede.304 Not Modified: Cache negociado atingido, validação bem-sucedida.- Tamanho do arquivo (ex:
1.5 kB): Requisição completa do servidor, cache não atingido ou expirado.
- Recarregue a página (Ctrl+R ou F5). Se o cache estiver configurado corretamente, muitos recursos estáticos mostrarão
(disk cache)ou304 Not Modified. - Use o cache do navegador para testes: desmarque "Disable cache" nas ferramentas do desenvolvedor para ver o comportamento normal. Limpe o cache do navegador (Ctrl+Shift+Delete) para simular um primeiro acesso.
Configuração Completa para Produção (Exemplo)
http {
# Definição de zona de cache para proxy reverso
# proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
listen 80;
server_name seu-dominio.com;
root /var/www/html;
# 1. Recursos Estáticos Versionados (Vite/Webpack) - Cache Permanente
location ~* .*-[a-f0-9]{8}\.(js|css|mjs|ts|jsx|tsx|wasm)$ {
expires max; # Define Expires para um tempo muito distante
add_header Cache-Control "public, immutable, max-age=31536000"; # 1 ano
add_header Vary "Accept-Encoding"; # Importante para caches que lidam com compressão
access_log off;
gzip_static on; # Se usar ficheiros .gz pré-compressos
}
# 2. Imagens e Mídia - Cache de Longo Prazo
location ~* \.(jpg|jpeg|png|gif|ico|webp|svg|avif|mp4|webm)$ {
expires 1y; # Cache por 1 ano
add_header Cache-Control "public, max-age=31536000";
add_header Vary "Accept-Encoding";
access_log off;
}
# 3. Fontes - Cache de Longo Prazo
location ~* \.(woff|woff2|ttf|eot|otf)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000";
add_header Access-Control-Allow-Origin "*"; # CORS se necessário
access_log off;
}
# 4. Arquivos HTML - Sem Cache
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# 5. APIs (Exemplo com Proxy Reverso)
# location /api/static/ {
# proxy_pass http://backend_server;
# proxy_cache api_cache; # Usa a zona de cache definida em http block
# proxy_cache_valid 200 302 10m; # Cache de 10 minutos para códigos 200 e 302
# proxy_cache_valid 404 1m; # Cache de 1 minuto para 404
# add_header X-Cache-Status $upstream_cache_status;
# add_header Cache-Control "public, max-age=600"; # Cache de 10 minutos
# }
# location /api/dynamic/ {
# proxy_pass http://backend_server;
# add_header Cache-Control "private, max-age=60, must-revalidate"; # Cache privado de 1 minuto
# }
# 6. Service Worker - Sem Cache
location /sw.js {
add_header Cache-Control "no-cache, max-age=0";
}
# 7. Locação Padrão (Para SPA, etc.)
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache"; # Geralmente o HTML é servido sem cache
}
}
}