06 de ago. de 2025·8 min de leitura

Cache em CDN para Next.js: cabeçalhos de cache sem vazamento de usuários

Cache em CDN para Next.js acelera páginas e assets, mas cabeçalhos incorretos podem armazenar dados de usuários. Aprenda o que cachear, exemplos de cabeçalhos e armadilhas.

Cache em CDN para Next.js: cabeçalhos de cache sem vazamento de usuários

Por que o cache de CDN pode quebrar um app Next.js

CDNs fazem sites parecerem rápidos ao salvar cópias do que seu servidor envia e servir essas cópias a partir de um local perto do visitante. Feito corretamente, reduz o tempo de carregamento e o custo do servidor. Feito sem cuidado, pode quebrar logins, mostrar dados errados ou "consertar" sozinho mais tarde.

Um CDN pode armazenar dois tipos de coisa:

  • Arquivos (imagens, JS, CSS)
  • Respostas HTTP completas (HTML de uma página, JSON de uma rota de API)

A parte difícil é que um app Next.js frequentemente serve páginas públicas e páginas específicas do usuário no mesmo domínio. Se o CDN tratar ambos do mesmo jeito, você pode acabar cacheando algo que nunca deveria ser compartilhado.

Dois problemas causam a maior parte das dores de cabeça:

  • Conteúdo obsoleto: você atualiza uma página, mas as pessoas ainda veem a versão antiga porque ela estava em cache.
  • Respostas pessoais cacheadas: o CDN salva uma resposta destinada a uma pessoa e depois a entrega a outra.

Um erro comum é o seguinte: você tem uma página inicial de marketing e um dashboard em /app. Um usuário faz login, visita /app e o servidor retorna HTML que inclui o nome dele e atividades recentes. Se esse HTML for cacheado, o próximo visitante que acessar /app pode ver a interface do primeiro usuário, ou ficar preso em um loop de autenticação quebrado.

Portanto, cache de CDN para Next.js é menos sobre "ligar o cache" e mais sobre definir regras claras. Decida o que é seguro cachear (normalmente assets estáticos e páginas realmente públicas), o que nunca deve ser cacheado (HTML personalizado, fluxos de autenticação e a maioria das APIs específicas de usuário) e por quanto tempo o conteúdo em cache deve permanecer.

Mapeie seu app em partes cacheáveis e não cacheáveis

Antes de mexer em cabeçalhos, mapeie o que seu app realmente serve. O cache do CDN funciona melhor quando você trata cada tipo de resposta de forma diferente. Uma regra única para tudo é como dados de usuário acabam sendo cacheados por engano.

Comece listando os principais tipos de resposta que você retorna:

  • Arquivos estáticos (JS, CSS, fontes)
  • Imagens (incluindo variantes otimizadas)
  • Páginas HTML (marketing, docs, dashboards)
  • Respostas de API (JSON, webhooks, callbacks de auth)

Depois, agrupe suas rotas pelo quão públicas elas realmente são:

Grupo de rotasExemplosPode ser cacheado no CDN?Muda com frequência?
Público/, /pricing, /blog/*Normalmente simÀs vezes
Semi-público/account, /checkoutNormalmente nãoFrequentemente
Privado/dashboard, /adminNãoFrequentemente

Agora marque as áreas de "alto churn". Preços, inventário e qualquer coisa que mostre status em tempo real (pedidos, uso, mensagens) podem ficar obsoletos rápido. Dashboards são o óbvio, mas páginas de checkout, etapas de onboarding e até blocos de "Bem-vindo de volta" podem ser personalizados.

Diferentes tipos de conteúdo precisam de regras diferentes porque os riscos são distintos. Cachear agressivamente um bundle JS geralmente é seguro. Cachear HTML que depende de cookies pode vazar a página de um usuário para outro.

Um exemplo real: um site tem uma homepage pública, mas o cabeçalho mostra o nome do usuário quando logado. Esse detalhe transforma uma página que seria cacheável em um risco, a menos que você separe a experiência (shell público + dados privados buscados separadamente) ou desative o cache quando cookies estiverem presentes.

Cacheie assets estáticos com segurança (JS, CSS, fontes, imagens)

Assets estáticos são a vitória mais fácil porque normalmente não mudam por usuário. Pense em arquivos baixados pelo navegador e reaproveitados em muitas páginas.

Assets típicos incluem:

  • Arquivos de build do Next.js em /_next/static/ (chunks JS, CSS)
  • Fontes e ícones (woff2, ttf, svg)
  • Imagens e favicons
  • Qualquer coisa na sua pasta public/

Para arquivos versionados (aqueles com hash no nome), cache longo é o padrão certo. O hash muda quando o conteúdo muda, então o arquivo é efetivamente imutável.

Um cabeçalho comum para esses é:

Cache-Control: public, max-age=31536000, immutable

Antes de definir TTL de um ano, certifique-se de que "immutable" é realmente verdade no seu fluxo. Se você serve logo.png de public/ e depois substitui o arquivo mantendo o mesmo nome, um TTL longo pode manter o logo antigo em caches por muito tempo. Para nomes não versionados, mantenha TTLs mais curtos ou adicione um passo de build que fingerprint os nomes dos arquivos.

Imagens frequentemente precisam de sua própria estratégia. Se você gera múltiplos tamanhos ou formatos (como WebP e AVIF), o cache é seguro apenas quando a URL final identifica unicamente a variante. Se um endpoint otimizado varia por largura, qualidade ou formato, essas entradas devem fazer parte da chave de cache. Caso contrário, usuários podem receber a variante errada.

Um chek rápido: escolha um chunk JS e uma fonte, abra-os e confirme que eles só mudam quando você faz um novo deploy.

Cache de páginas e HTML: o que é seguro e o que é arriscado

O maior ganho (e o maior risco) é cachear HTML completo. HTML é o que o usuário vê, então se você cachear a coisa errada, pode mostrar a pessoa errada o conteúdo errado.

HTML seguro para cachear é normalmente conteúdo igual para todo mundo: páginas de marketing, pricing, docs, landing pages públicas, a maioria dos posts do blog. Um teste simples: abra a página numa janela anônima. Se ela parecer igual logado e deslogado, é um bom candidato.

HTML arriscado para cachear é qualquer coisa que mude por usuário, por sessão ou com base em dados privados: dashboards, configurações de conta, checkout, histórico de pedidos, páginas que mostram nome, e‑mail, itens salvos, status de cobrança ou "você fez login em...". Cachear isso no CDN é como acontecem vazamentos de dados.

Uma regra prática:

  • Cacheie páginas públicas com tempos controlados.
  • Não cacheie páginas de usuário a menos que tenha 100% de certeza de que a resposta é idêntica para todo mundo.
  • Na dúvida, trate o HTML como privado e desative o cache.

ISR (Incremental Static Regeneration) fica no meio termo. Ele permite que o Next.js sirva uma página em cache rapidamente e a atualize em segundo plano num cronograma. É ótimo para posts de blog que mudam ocasionalmente. Não é adequado para páginas pessoais, porque frescor não resolve o problema de "usuário errado".

Mais uma pegadinha: cachear HTML não é o mesmo que cachear chamadas de dados. Uma página pode ser segura para cache enquanto uma API que ela chama não é. Ou você pode manter o HTML privado, mas cachear dados verdadeiramente públicos (como listagem de produtos) com TTL curto. Misturar isso é como "funcionou em staging" virar conteúdo obsoleto ou exposição acidental.

Cabeçalhos de cache que você precisa conhecer (sem jargão)

A maioria dos problemas de cache vem de uma coisa: o CDN e o navegador adivinham por quanto tempo algo é seguro para reutilizar. Cabeçalhos de cache são como você diz isso claramente.

Cache-Control: o principal

Cache-Control é um conjunto de instruções. As partes mais comuns são:

  • max-age=...: quanto tempo o navegador pode guardar (segundos)
  • s-maxage=...: quanto tempo caches compartilhados (CDN) podem guardar. Se presente, CDNs geralmente seguem isso em vez de max-age.
  • public: OK para armazenar em cache compartilhado
  • private: só o navegador do usuário pode armazenar (o CDN não deve)
  • no-store: não armazenar em nenhum lugar (use para respostas pessoais ou sensíveis)

Uma regra simples: se a resposta puder alguma vez diferir por usuário, evite public. Para dashboards e páginas de conta, prefira no-store a menos que tenha um motivo muito específico para não usar.

stale-while-revalidate: mostrar o antigo, atualizar em segundo plano

stale-while-revalidate=... permite que um cache sirva uma versão ligeiramente antiga por um curto período enquanto busca uma nova.

Funciona bem para conteúdo que pode ficar um pouco defasado (como uma homepage de marketing). É arriscado para qualquer coisa que precise estar correta naquele momento (como cobrança ou permissões).

ETag e Last-Modified: como caches verificam mudanças

Com ETag ou Last-Modified, um cache pode perguntar: "isso mudou?" Se não, o servidor responde com um pequeno retorno "not modified" em vez de reenviar o corpo completo.

Isso importa principalmente quando respostas são grandes, o conteúdo atualiza com frequência ou você quer atualizações rápidas sem desligar o cache.

Vary: o rótulo de "depende de"

Vary diz aos caches quais cabeçalhos da requisição podem alterar a resposta. Isso importa quando cookies ou auth estão envolvidos.

Se o HTML muda com base no estado de login, e você não trata Vary junto com regras de cache corretamente, um CDN pode servir a versão de um usuário para outro. Vary comuns incluem Cookie, Authorization e Accept-Encoding.

Passo a passo: definir regras de cache para um app Next.js típico

Previna vazamento de dados de usuários
Encontramos onde cookies ou cabeçalhos de autenticação estão causando HTML ou JSON cacheados de forma arriscada.

Uma forma prática é começar pelas coisas mais seguras e depois avançar para conteúdo que pode mudar.

1) Comece pelos alvos mais seguros

Inicie com arquivos que são iguais para todo mundo: bundles JavaScript, CSS, fontes e imagens versionadas. São baixo risco e geralmente trazem o maior ganho de velocidade.

2) Defina tempos de cache conforme o que pode mudar

Use cache muito longo para assets imutáveis (arquivos que mudam de nome quando o conteúdo muda). Para páginas e HTML, use cache mais curto, pois o conteúdo pode atualizar sem mudar o nome do arquivo.

Uma checklist prática:

  • Comece com /_next/static/*, seus assets claramente versionados e outros arquivos com nomes hashed.
  • Para assets imutáveis, envie um Cache-Control longo com immutable.
  • Para páginas públicas não personalizadas (marketing, docs), use um cache compartilhado mais curto (minutos a horas) e considere stale-while-revalidate.
  • Para qualquer coisa específica do usuário (dashboards, páginas de conta, rotas de API que dependem de cookies), force Cache-Control: private ou no-store.
  • Decida como você invalidará cache: confie em deploys que mudam nomes de assets e tenha um plano claro de purge para HTML quando publicar atualizações urgentes.

3) Teste antes de confiar

Não pare em "carrega rápido". Faça um hard refresh e compare comportamento deslogado vs logado.

Um teste rápido: abra a mesma página em uma janela anônima e em uma sessão logada. Se HTML, cabeçalhos ou dados visíveis coincidirem quando não deveriam, trate como vazamento e pare de cachear essa rota.

Chaves de cache do CDN e regras que decidem o que é servido

Um CDN não "cacheia uma página" de forma abstrata. Ele guarda uma resposta sob uma chave de cache. Essa chave normalmente é baseada no caminho da requisição e, às vezes, na query string, cabeçalhos e cookies.

Se a chave for ampla demais, usuários verão conteúdo errado. Se for muito específica, você terá baixa taxa de acerto de cache e páginas mais lentas.

O que normalmente pertence à chave de cache

Comece simples: para páginas públicas, o path costuma ser suficiente. Adicione outras partes só quando realmente mudarem o que o usuário deve ver.

  • Path: /pricing vs /blog/slug devem ser separados.
  • Query params: inclua apenas os que alteram conteúdo (por exemplo, ?page=2). Ignore parâmetros de tracking como utm_*.
  • Headers: inclua apenas variantes reais (por exemplo, idioma).
  • Cookies: evite usar cookies para conteúdo público. Eles explodem o número de variantes de cache.

O caso perigoso é quando um CDN cacheia uma resposta personalizada porque havia um cookie presente. Mesmo que o cookie não faça parte da chave, cachear uma resposta personalizada ainda pode vazar dados.

Variantes: local e dispositivo

Se seu HTML muda por localidade, use um sinal claro e varie por ele (frequentemente Accept-Language ou um cabeçalho dedicado que você controla).

Cuidado com variações por dispositivo. Variar por User-Agent cria muitas versões e é fácil errar. Prefira design responsivo ou um cabeçalho explícito pequeno se realmente precisar de HTML separado.

Por fim, defina regras de bypass para qualquer coisa que nunca deva ser cacheada: /admin, rotas /api que retornam dados pessoais, dashboards autenticados e qualquer rota que verifique um cookie de sessão.

Como evitar cachear respostas personalizadas

Deploy com confiança
Preparamos seu app para deploys de produção sem surpresas relacionadas a cache.

Um CDN é ótimo até salvar acidentalmente uma resposta destinada a uma pessoa e entregá‑la a outra. Esse é o maior risco: HTML ou JSON cacheados podem se tornar um vazamento de dados se incluírem nome, e‑mail, status de cobrança ou qualquer coisa ligada à sessão do usuário.

Personalização normalmente entra por cookies e cabeçalhos de auth. Se uma requisição inclui Cookie ou Authorization, assuma que ela pode produzir saída específica do usuário, a menos que você tenha total certeza do contrário.

Use cabeçalhos de cache claros para qualquer coisa atrás de login. Para dashboards, configurações, páginas de cobrança e rotas de API de usuário, prefira Cache-Control: no-store (ou pelo menos private, no-store). Isso diz a navegadores e CDNs para não manter uma cópia.

Sinais práticos que devem levar você a não cachear:

  • A requisição inclui Cookie, Authorization ou um header de token de sessão.
  • A resposta depende de quem é o usuário (mesmo que a URL seja a mesma).
  • O HTML inclui dados de conta, atividade recente ou estado de cobrança.
  • Uma API retorna registros de usuário, tokens ou qualquer coisa sensível.
  • Você não consegue explicar a chave de cache do seu CDN em uma frase.

Páginas mistas são complicadas. Um padrão comum é um shell público de marketing que também mostra "Oi Sam" ou a contagem de notificações quando logado. Se o HTML for personalizado, não o cacheie publicamente. Um padrão mais seguro: cacheie o shell público e busque os fragmentos personalizados do lado do cliente (ou de um endpoint separado não cacheável).

Se puder, separe endpoints públicos e privados para que suas regras fiquem simples. Mantenha rotas cacheáveis claramente públicas e rotas autenticadas claramente não cacheáveis.

Erros comuns que causam conteúdo obsoleto ou vazamentos de dados

A maioria das falhas de cache acontece quando algo "pareceu rápido" num teste rápido, mas se comporta diferente quando usuários reais fazem login. O perigo não é só páginas obsoletas. É também servir o conteúdo de uma pessoa para outra.

Erros recorrentes:

  • Cachear HTML para rotas de usuário porque parecia seguro em testes anônimos.
  • Marcar respostas como public embora mudem com cookies, um header de auth ou geolocalização.
  • Dar max-age longo para assets que não são versionados (como /app.css), então ao enviar uma atualização os usuários ficam com o arquivo antigo.
  • Esquecer que redirects, páginas de erro e 404s também podem ser cacheados, o que pode travar uma queda temporária ou uma rota errada.
  • Confiar no comportamento padrão de um serviço de hospedagem ou do framework sem checar o que o CDN realmente está armazenando.

Uma forma rápida de achar risco: pergunte, "essa resposta pode ser diferente para dois usuários?" Se sim, não deve ser cacheada publicamente. Mesmo uma diferença pequena como uma saudação significa que o HTML é personalizado.

Também fique atento a cache invisível: um 302 para /login cacheado, um 404 cacheado para uma página recém-lançada ou um 500 cacheado durante um deploy.

Verificações rápidas antes de colocar cache em produção

Antes de liberar o cache, faça uma checagem com requisições reais, não apenas o que você espera que o código faça. Pequenos erros de cabeçalho podem virar páginas obsoletas ou, pior, o usuário errado vendo conteúdo pessoal.

Comece pelo básico:

  • Páginas públicas iguais para todos devem enviar Cache-Control: public e um s-maxage sensato para que o CDN possa cacheá‑las.
  • Qualquer coisa atrás de login deve ser Cache-Control: private ou no-store.

Se estiver inseguro, use no-store por padrão e afrouxe depois.

Checagens pré‑deploy que pegam a maioria dos problemas:

  • Abra uma página pública e confirme que ela é realmente cacheável (Cache-Control: public com s-maxage).
  • Abra uma página autenticada e confirme que ela não é cacheável por cache compartilhado (private ou no-store, nunca public).
  • Verifique assets estáticos: cache longo só é seguro quando filenames são versionados (hash no nome). Se não, mantenha cache curto.
  • Acesse APIs que retornam dados de usuário e confirme que não estão cacheáveis pelo CDN.
  • Teste duas contas: faça login como Conta A em um navegador, Conta B em outro, e carregue as mesmas URLs. Nada pessoal deve cruzar entre elas.

Também observe parâmetros de query. Se seu CDN faz cache pela URL completa, ?ref=, ?utm_ e filtros aleatórios podem criar variantes infinitas de cache. Decida quais params devem ser ignorados e quais devem fazer parte da chave.

Um exemplo realista: site de marketing + dashboard no mesmo domínio

Correções com entrega rápida
A maioria das correções chega em 48 a 72 horas após identificarmos os problemas com você.

Uma startup lança um app Next.js que mistura um site de marketing público e um dashboard logado no mesmo domínio. O tráfego sobe após lançamentos, então adicionam cache no CDN para manter as páginas rápidas.

Eles dividem o app em dois grupos.

Cacheie o que é seguro e idêntico para todos:

  • JS e CSS versionados (cache longo, porque filenames mudam no deploy)
  • Imagens, fontes e ícones (cache longo se filenames forem versionados; mais curto se sobrescritos)
  • Páginas de marketing como /, /pricing, /blog (TTL curto no CDN para que edições apareçam rápido)

Para HTML de marketing, eles colocam TTL curto no cache compartilhado e uma janela pequena de stale-while-revalidate. O CDN pode servir uma página ligeiramente antiga por um momento enquanto atualiza.

Nunca cacheie o que varia por usuário:

  • /dashboard e tudo abaixo dele
  • Páginas de configurações e cobrança
  • /api/user (e qualquer rota que retorne dados de conta)
  • Páginas de autenticação e callbacks

Para verificar que não criaram vazamento de usuário, eles testam com paranoia: duas contas, dois navegadores, hard refresh e checagem de cabeçalhos. Páginas personalizadas nunca devem mostrar diretivas public de cache.

Quando algo dá errado (um usuário vê o nome de outro ou status de assinatura desatualizado), eles regridem a primeira coisa: a regra do CDN. Depois apertam os cabeçalhos nas rotas de risco (começando por /dashboard e /api) antes de reabilitar qualquer cache.

Próximos passos: lançar com segurança e pedir ajuda se estiver bagunçado

Comece pequeno e torne as vitórias entediantes. Cacheie assets estáticos imutáveis (JS e CSS com hash, fontes, imagens versionadas) com TTLs longos. Quando isso estiver estável, adicione cache para páginas verdadeiramente públicas. Trate qualquer coisa que possa variar por usuário, cookie ou auth como não cacheável, a menos que você tenha projetado especificamente para edge caching.

Escreva suas regras de cache em inglês simples. Muitos bugs de cache aparecem meses depois quando alguém adiciona um cabeçalho novo, introduz um redirect ou muda a autenticação. Um pequeno "contrato de cache" facilita revisões: o que é cacheável, o que nunca pode ser cacheado e quais sinais (cookies, cabeçalhos, query params) mudam a resposta.

Se você herdou um codebase gerado por IA, revise cuidadosamente cabeçalhos e fluxos de autenticação. Esses projetos frequentemente vêm com roteamento misto, Cache-Control inconsistente e sessões que funcionam localmente mas quebram atrás de um CDN.

Se você está vendo autenticação quebrada, comportamento de cache arriscado ou não consegue explicar com confiança o que seu CDN está armazenando, FixMyMess (fixmymess.ai) faz diagnóstico de codebase e reparos para apps gerados por IA, incluindo desembaraçar cabeçalhos de cache e limites de autenticação. Eles também oferecem uma auditoria de código gratuita para identificar problemas antes de você mudar algo em produção.