Pular para o conteúdo

CDN e cache

Intermediário14 min de leituraredes

CDN resolve três problemas ao mesmo tempo: latência (o conteúdo fica perto de quem pede), carga (a origem para de servir o que se repete) e custo (saída pela CDN costuma ser mais barata que saída direta da região). Para quem atende o Brasil inteiro a partir de São Paulo, a diferença de latência para o Norte e Nordeste é grande o suficiente para aparecer nas métricas de negócio.

O preço é um novo lugar onde as coisas ficam desatualizadas — e quase todo problema de CDN é, no fundo, um problema de chave de cache.

A CDN obedece aos cabeçalhos que a sua aplicação envia. Configure na origem:

# asset com nome versionado (app.9f3a2c.js) — cacheie forte e para sempre
Cache-Control: public, max-age=31536000, immutable
# HTML que muda: revalide sempre, mas permita servir enquanto revalida
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=300
# conteúdo por usuário: nunca em cache compartilhado
Cache-Control: private, no-store

Três diretivas que resolvem a maior parte dos casos:

  • s-maxage vale só para o cache compartilhado (CDN), independente do max-age do navegador. É o que permite cache longo na borda e curto no cliente.
  • stale-while-revalidate serve a versão antiga enquanto busca a nova — o usuário nunca espera pela origem.
  • stale-if-error continua servindo o conteúdo velho quando a origem cai. É disponibilidade de graça: ative.

A chave define o que conta como “a mesma resposta”. Por padrão é a URL — e os problemas começam quando a resposta varia por algo que não está nela.

  • Query string: ?utm_source=... cria uma entrada nova para cada campanha e destrói a taxa de acerto. Ignore parâmetros de rastreamento na chave.
  • Cabeçalhos: se a resposta muda por idioma ou dispositivo, declare Vary: Accept-Language. Vary: User-Agent fragmenta o cache em milhares de variantes — evite.
  • Cookie: um cookie de sessão presente na requisição costuma desabilitar o cache inteiro. Separe o domínio dos assets estáticos, ou configure a CDN para ignorar cookies em caminhos estáticos.

O erro grave a evitar: servir de um cache compartilhado uma página que contém dado de um usuário. Isso vaza informação de uma pessoa para outra. Toda resposta autenticada precisa de Cache-Control: private, no-store — e vale testar isso explicitamente.

Janela do terminal
curl -sI https://loja.exemplo.com.br/app.9f3a2c.js | grep -iE 'cache-control|age|x-cache'
# x-cache: HIT/MISS (o nome varia por CDN) diz se veio da borda

Invalidação global é lenta, às vezes cobrada, e é a solução errada para o caso comum. Prefira versionar o nome do arquivo: app.9f3a2c.js muda de nome a cada build, então nunca precisa ser invalidado, e o navegador nunca serve o antigo.

Guarde a invalidação para o que não pode ser versionado — HTML de entrada, sitemap.xml, uma imagem substituída às pressas:

Janela do terminal
aws cloudfront create-invalidation --distribution-id E123 --paths '/index.html' '/'

E invalide caminhos específicos: /* esvazia tudo, faz a origem receber uma avalanche de requisições e pode derrubar o serviço no momento seguinte.

Se a origem responde direto na internet, alguém vai encontrá-la e passar por cima da CDN — inclusive um ataque. Feche o caminho:

  • Aceite tráfego apenas dos IPs da CDN, ou exija um cabeçalho secreto compartilhado.
  • Use origem privada quando o provedor suportar (OAC/Origin Access, Private Link).
  • Ative coalescing de requisições (a CDN faz uma só requisição à origem para muitos pedidos simultâneos do mesmo objeto) — é o que evita a estampida quando um item popular expira.
  • Deixe a CDN aplicar limite de taxa e WAF antes da origem.

Dá para cachear resposta de API e HTML dinâmico, e o ganho é grande — desde que:

  • A resposta seja igual para todo mundo naquele contexto (produto, categoria, home deslogada).
  • O TTL seja curto (5 a 60 segundos) e combinado com stale-while-revalidate.
  • Exista invalidação por tag/surrogate-key quando o dado muda, para não esperar o TTL.
  • Nada autenticado ou personalizado entre no cache compartilhado.

Um TTL de 10 segundos na home costuma cortar a maior parte do tráfego da origem em pico — poucas coisas em operação têm essa relação custo-benefício.

Por fim, monitore a taxa de acerto por caminho. Queda súbita quase sempre significa mudança de chave de cache (um parâmetro novo, um cookie que passou a ser enviado, um Vary acrescentado), e ela chega à origem como aumento de carga sem aumento de usuários.

Próximo passo: feche a trilha com o modelo de acesso que substitui o perímetro em Zero trust na prática.