Back

Por Que Seu Cache do Next.js Não Funciona (E Como Resolver em 2026)

Já passou por isso desenvolvendo com Next.js? "Atualizei os dados mas continuam mostrando o mesmo..." "Configurei revalidate mas não funciona..." "Local funciona perfeito mas produção não..." Relaxa, você não é o único. O cache do App Router do Next.js é super poderoso, mas também é a parte mais confusa do framework.

Nesse artigo vamos dissecar cada camada de cache no Next.js 15 e 16, entender por que o cache se comporta de forma estranha às vezes, e te dar soluções que realmente funcionam na prática.

As 4 camadas de cache do Next.js (o básico)

Antes de começar a debugar, tem algo importante que você precisa saber: o App Router do Next.js não tem UM cache, tem 4 camadas de cache independentes. Cada uma faz algo diferente, e confundir elas é a raiz da maioria dos problemas.

1. Request Memoization (Memoização de Requisições)

Escopo: Passada de renderização única (apenas server-side)
Duração: Duração de uma única requisição
Propósito: Deduplicar fetches de dados idênticos dentro de uma única renderização

// Ambas as chamadas fetch são automaticamente deduplicadas // Apenas UMA requisição HTTP real é feita! async function ProductDetails({ id }: { id: string }) { const product = await fetch(`/api/products/${id}`); return <ProductPrice productId={id} />; } async function ProductPrice({ productId }: { productId: string }) { // Esse mesmo fetch é memoizado—sem requisição duplicada const product = await fetch(`/api/products/${productId}`); return <span>${product.price}</span>; }

A memoização de requisições acontece automaticamente para chamadas fetch com a mesma URL e opções durante uma única renderização do servidor. Esse é o comportamento nativo do React estendido pelo Next.js.

Conceito errado comum: Isso NÃO é cache persistente. Uma vez que a requisição completa, essa memoização desaparece. Ela só previne fetches duplicados dentro da mesma travessia da árvore de renderização.

2. Data Cache (Cache de Dados)

Escopo: Server-side, persistente
Duração: Até revalidação ou invalidação manual
Propósito: Cachear respostas de fetch através de requisições e deploys

// Cacheado indefinidamente (comportamento estático) const data = await fetch('https://api.example.com/data'); // Cacheado por 60 segundos const data = await fetch('https://api.example.com/data', { next: { revalidate: 60 } }); // Nunca cacheado const data = await fetch('https://api.example.com/data', { cache: 'no-store' });

O Data Cache é onde a maior parte da confusão acontece. Ele armazena a resposta crua das chamadas fetch e persiste através de:

  • Múltiplas requisições de usuários
  • Reinicializações do servidor (em produção com infraestrutura adequada)
  • Deploys (em plataformas como Vercel)

3. Full Route Cache (Cache Completo de Rota)

Escopo: Server-side, persistente
Duração: Até revalidação
Propósito: Cachear o HTML completo e o payload RSC (React Server Component) para rotas

Quando você builda uma aplicação Next.js, rotas estáticas são pré-renderizadas no tempo de build. O Full Route Cache armazena:

  • O HTML renderizado para carregamento inicial da página
  • O Payload RSC para navegação do lado do cliente
// Essa página é gerada estaticamente e totalmente cacheada export default function AboutPage() { return <div>Sobre Nós</div>; } // Essa página opta por sair do Full Route Cache export const dynamic = 'force-dynamic'; export default function DashboardPage() { return <div>Dashboard: {new Date().toISOString()}</div>; }

4. Router Cache (Cache do Router, client-side)

Escopo: Client-side, por sessão
Duração: Baseado em sessão com invalidação automática
Propósito: Cachear segmentos de rota visitados no navegador para navegação instantânea voltar/avançar

Usuário visita /products → Router Cache armazena layout + página de /products Usuário navega para /products/123 → Router Cache armazena página de /products/123 → Layout de /products é reutilizado do cache Usuário clica no botão voltar → Página de /products servida instantaneamente do Router Cache

O Router Cache mudou bastante no Next.js 15 e é o que mais gera confusão. Explicamos melhor abaixo.

Como debugar quando o cache não funciona

Quando os dados não atualizam, siga esses passos:

Passo 1: Identificar Qual Camada de Cache Está Envolvida

Faça a si mesmo essas perguntas:

PerguntaSe SimSe Não
Os dados desatualizados aparecem imediatamente no primeiro carregamento?Data Cache ou Full Route CacheRouter Cache (client-side)
O hard refresh (Cmd+Shift+R) resolve?Router CacheCache server-side
Fazer redeploy resolve?Full Route CacheConfiguração errada do Data Cache
É em desenvolvimento?Servidor dev tem cache diferenteVerificar build de produção

Passo 2: Verificar Sua Configuração de Fetch

O problema mais comum é entender mal os valores padrão do cache do fetch.

// ❌ Erro comum: assumir que isso é dinâmico const res = await fetch('https://api.example.com/user'); // Isso é CACHEADO POR PADRÃO no Next.js! // ✅ Correto: optar explicitamente por sair do cache const res = await fetch('https://api.example.com/user', { cache: 'no-store' }); // ✅ Ou usar revalidação baseada em tempo const res = await fetch('https://api.example.com/user', { next: { revalidate: 0 } // Revalidar em cada requisição });

Passo 3: Entender a Configuração do Segmento de Rota

A configuração do segmento de rota afeta como toda a rota é cacheada:

// app/dashboard/page.tsx // Forçar toda a rota a ser dinâmica export const dynamic = 'force-dynamic'; // Forçar geração estática (vai dar erro se funções dinâmicas forem usadas) export const dynamic = 'force-static'; // Definir revalidação para toda a rota export const revalidate = 60; // Revalidar a cada 60 segundos // Desabilitar todo o cache export const revalidate = 0; export const fetchCache = 'force-no-store';

Armadilhas que não estão na documentação

Armadilha #1: cookies() e headers() Optam Automaticamente por Sair

Quando você usa funções dinâmicas em um Server Component, toda a rota se torna dinâmica:

import { cookies } from 'next/headers'; export default async function UserPage() { // Apenas chamar cookies() torna essa rota dinâmica, // mesmo se você não usar o resultado! const cookieStore = await cookies(); // Esse fetch agora também é dinâmico const user = await fetch('/api/user'); return <div>{user.name}</div>; }

A Solução: Se você precisa de cookies mas quer cache, reestruture seu componente:

// Separar as partes dinâmicas e estáticas import { Suspense } from 'react'; export default function UserPage() { return ( <div> <StaticHeader /> {/* Essa parte está cacheada */} <Suspense fallback={<Loading />}> <DynamicUserContent /> {/* Apenas isso é dinâmico */} </Suspense> </div> ); }

Armadilha #2: O Paradoxo do searchParams

Acessar searchParams em uma página a torna dinâmica, mesmo se você não usar parâmetros de URL:

// ❌ Essa página é dinâmica mesmo se nenhum search param for passado export default function ProductsPage({ searchParams, }: { searchParams: { sort?: string }; }) { // Apenas a presença de searchParams nas props = dinâmico return <ProductGrid />; } // ✅ Melhor: só acessar searchParams quando necessário export default function ProductsPage() { return <ProductGrid />; // Estático por padrão } // Para views filtradas, usar uma rota separada ou componente cliente

Armadilha #3: Requisições POST e Data Cache

Requisições POST têm um comportamento de cache único que confunde muitos desenvolvedores:

// Requisições POST NÃO são cacheadas por padrão const res = await fetch('/api/data', { method: 'POST', body: JSON.stringify({ id: 1 }), }); // Mas se a resposta usar headers de cache, ela PODE ser cacheada! // Verifique os headers de resposta da sua API!

Armadilha #4: A Discrepância Desenvolvimento vs Produção

Essa é talvez a armadilha mais frustrante. Em desenvolvimento:

  • Data Cache está desabilitado por padrão
  • Full Route Cache não existe
  • Hot reload às vezes limpa caches inesperadamente
# Sempre teste o comportamento do cache com um build de produção npm run build && npm start

Se seus problemas de cache só aparecem em produção, essa é a razão.

Armadilha #5: Timing do ISR e revalidate

revalidate não significa "atualizar a cada N segundos"—significa "após N segundos, a PRÓXIMA requisição vai disparar uma regeneração em background":

export const revalidate = 60; // Linha do tempo: // t=0: Página gerada, cacheada // t=30: Usuário visita → recebe versão cacheada (30s de idade) // t=65: Usuário visita → recebe versão cacheada (65s de idade), dispara regeneração em background // t=66: Novo cache pronto // t=70: Usuário visita → recebe versão fresca

O padrão stale-while-revalidate significa que usuários podem ver conteúdo desatualizado mesmo após o período de revalidação.

Soluções prontas pra usar

Receita 1: Dados de Dashboard em Tempo Real

Problema: Dashboard mostra dados desatualizados mesmo após atualizações.

// app/dashboard/page.tsx import { unstable_noStore as noStore } from 'next/cache'; export default async function Dashboard() { noStore(); // Optar por sair de todo cache para esse componente const stats = await fetchDashboardStats(); return <DashboardView stats={stats} />; } // Ou usar config de segmento de rota export const dynamic = 'force-dynamic'; export const revalidate = 0;

Receita 2: Conteúdo Específico de Usuário com Layout Compartilhado

Problema: Layout está cacheado, mas conteúdo da página precisa ser específico do usuário.

// app/dashboard/layout.tsx // Esse layout pode permanecer estático export default function DashboardLayout({ children }) { return ( <div className="dashboard-container"> <Sidebar /> {/* Cacheado */} {children} </div> ); } // app/dashboard/page.tsx import { cookies } from 'next/headers'; export default async function DashboardPage() { const cookieStore = await cookies(); const userId = cookieStore.get('userId')?.value; // Essa página é dinâmica, mas o layout ainda está cacheado const userData = await fetch(`/api/users/${userId}`, { cache: 'no-store' }); return <UserDashboard data={userData} />; }

Receita 3: Páginas de Produto E-Commerce com Atualizações de Preço

Problema: Mudanças de preço precisam ser refletidas em 5 minutos, mas descrições de produtos podem ser cacheadas por mais tempo.

// app/products/[id]/page.tsx export const revalidate = 300; // 5 minutos export default async function ProductPage({ params }) { // Detalhes do produto revalidam a cada 5 minutos const product = await fetch(`/api/products/${params.id}`, { next: { revalidate: 300 } }); // Estoque deve ser em tempo real const inventory = await fetch(`/api/inventory/${params.id}`, { cache: 'no-store' }); return <ProductView product={product} inventory={inventory} />; }

Receita 4: Blog com Revalidação On-Demand

Problema: Posts do blog devem ser cacheados, mas revalidar quando o conteúdo é atualizado no CMS.

// app/blog/[slug]/page.tsx export const revalidate = false; // Cachear indefinidamente export default async function BlogPost({ params }) { const post = await fetch(`/api/posts/${params.slug}`, { next: { tags: [`post-${params.slug}`] } }); return <Article post={post} />; } // app/api/revalidate/route.ts import { revalidateTag } from 'next/cache'; import { NextRequest } from 'next/server'; export async function POST(request: NextRequest) { const { slug, secret } = await request.json(); if (secret !== process.env.REVALIDATION_SECRET) { return Response.json({ error: 'Secret inválido' }, { status: 401 }); } revalidateTag(`post-${slug}`); return Response.json({ revalidated: true }); }

Receita 5: Corrigindo Problemas do Router Cache (Next.js 15)

Problema: Navegação client-side mostra dados desatualizados.

Desde o Next.js 15, o comportamento do Router Cache mudou significativamente. Páginas dinâmicas não são mais cacheadas no cliente por padrão, mas páginas estáticas ainda são. O Next.js 16 continua essa abordagem com refinamentos adicionais.

// Para rotas dinâmicas que precisam de dados frescos em cada navegação: import { useRouter } from 'next/navigation'; function RefreshButton() { const router = useRouter(); const handleRefresh = () => { router.refresh(); // Invalida o Router Cache para a rota atual }; return <button onClick={handleRefresh}>Atualizar Dados</button>; }

Para cache busting mais agressivo:

// middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const response = NextResponse.next(); // Prevenir Router Cache para rotas específicas if (request.nextUrl.pathname.startsWith('/dashboard')) { response.headers.set( 'Cache-Control', 'no-store, must-revalidate' ); } return response; }

Padrões Avançados: Estratégias de Cache Tags

Cache tags permitem criar relacionamentos entre dados cacheados e invalidá-los com precisão:

// lib/data.ts export async function getProducts(category: string) { return fetch(`/api/products?category=${category}`, { next: { tags: [ 'products', // Todos os produtos `category-${category}`, // Categoria específica ] } }); } export async function getProductById(id: string) { return fetch(`/api/products/${id}`, { next: { tags: [ 'products', `product-${id}`, ] } }); } // Quando um único produto é atualizado: revalidateTag(`product-${updatedProductId}`); // Quando categoria é reorganizada: revalidateTag(`category-${categoryName}`); // Opção nuclear - todos os produtos: revalidateTag('products');

Ferramentas e Técnicas de Debug

1. Inspeção de Headers de Cache

// Adicionar headers de debug de cache em desenvolvimento // next.config.js module.exports = { async headers() { return [ { source: '/(.*)', headers: [ { key: 'x-next-cache-status', value: process.env.NODE_ENV === 'development' ? 'disabled' : 'enabled', }, ], }, ]; }, };

2. Logging do Comportamento do Cache

// lib/fetch-with-logging.ts export async function fetchWithCacheLogging(url: string, options?: RequestInit) { const startTime = performance.now(); const response = await fetch(url, options); console.log({ url, cacheMode: options?.cache ?? 'default', revalidate: (options as any)?.next?.revalidate ?? 'não definido', responseTime: `${(performance.now() - startTime).toFixed(2)}ms`, fromCache: response.headers.get('x-cache') === 'HIT', }); return response; }

3. Indicador Visual de Cache

// components/CacheDebugBadge.tsx import { unstable_cache } from 'next/cache'; export function CacheDebugBadge() { if (process.env.NODE_ENV !== 'development') return null; const renderTime = new Date().toISOString(); return ( <div className="fixed bottom-4 right-4 bg-black text-white p-2 text-xs rounded"> Renderizado: {renderTime} </div> ); }

Mudanças de Cache no Next.js 15/16: O Que Há de Novo?

Next.js 15 introduziu várias mudanças importantes nos padrões de cache, e Next.js 16 (lançado em outubro de 2025) revoluciona o cache com uma abordagem completamente nova.

Mudanças no Next.js 15 (Ainda Relevantes)

1. Requisições fetch Não São Mais Cacheadas por Padrão

// Next.js 14 e anteriores: cacheado por padrão // Next.js 15+: NÃO cacheado por padrão // Para restaurar o comportamento antigo: const data = await fetch(url, { cache: 'force-cache' });

2. Route Handlers São Dinâmicos por Padrão

// Next.js 14 e anteriores: handlers GET eram cacheados // Next.js 15+: todos os handlers são dinâmicos por padrão // Para tornar um route handler estático: export const dynamic = 'force-static'; export async function GET() { return Response.json({ time: new Date().toISOString() }); }

3. Mudanças no Router Cache do Cliente

// Next.js 14 e anteriores: Páginas cacheadas por 30 segundos (dinâmico) ou 5 minutos (estático) // Next.js 15+: Páginas dinâmicas têm 0 tempo de staleness // Para configurar tempo de staleness: // next.config.js module.exports = { experimental: { staleTimes: { dynamic: 30, // segundos static: 180, // segundos }, }, };

Next.js 16: A Revolução do "use cache"

Next.js 16 introduz Cache Components com a diretiva "use cache"—a mudança de cache mais significativa desde que o App Router foi introduzido. Isso completa a visão do Partial Pre-Rendering (PPR).

A Mudança de Paradigma: De Implícito para Explícito

No Next.js 16, todo código dinâmico é executado no momento da requisição por padrão. O cache agora é completamente opt-in:

// Next.js 16: Esta página é dinâmica por padrão export default async function ProductPage({ params }) { const product = await fetch(`/api/products/${params.id}`); return <ProductView product={product} />; } // Para cachear, você DEVE usar "use cache" explicitamente "use cache" export default async function ProductPage({ params }) { const product = await fetch(`/api/products/${params.id}`); return <ProductView product={product} />; }

Usando "use cache" em Diferentes Níveis

// Cachear uma página inteira "use cache" export default async function BlogPostPage({ params }) { const post = await fetchPost(params.slug); return <Article post={post} />; } // Cachear um componente específico async function CachedSidebar() { "use cache" const categories = await fetchCategories(); return <Sidebar categories={categories} />; } // Cachear uma função async function getExpensiveData(id: string) { "use cache" // Este resultado será cacheado return await computeExpensiveData(id); }

Combinando com Cache Tags

"use cache" import { cacheTag } from 'next/cache'; export default async function ProductPage({ params }) { cacheTag(`product-${params.id}`); const product = await fetch(`/api/products/${params.id}`); return <ProductView product={product} />; } // Invalidar com revalidateTag import { revalidateTag } from 'next/cache'; revalidateTag(`product-${params.id}`);

Novo: cacheLife para Controle de Expiração

"use cache" import { cacheLife } from 'next/cache'; export default async function DashboardStats() { cacheLife('minutes'); // Perfis predefinidos: 'seconds', 'minutes', 'hours', 'days', 'weeks', 'max' const stats = await fetchStats(); return <StatsView stats={stats} />; } // Ou com valores customizados cacheLife({ stale: 60, // Servir stale por 60 segundos revalidate: 300, // Revalidar após 5 minutos expire: 3600, // Expirar após 1 hora });

Cache do Sistema de Arquivos Turbopack (Beta)

Next.js 16 faz do Turbopack o bundler padrão com cache do sistema de arquivos:

// next.config.js module.exports = { experimental: { turbo: { // Cache do sistema de arquivos para rebuilds mais rápidos persistentCaching: true, }, }, };

Isso melhora dramaticamente os tempos de startup e compilação ao armazenar artefatos do compilador no disco entre execuções.

Implicações de Performance: Quando Cachear O Quê

Entender cache não é apenas evitar bugs—é também otimizar performance:

Tipo de ConteúdoEstratégia RecomendadaRazão
Páginas de marketingrevalidate: 3600 ou estático em buildMuda raramente, maximiza hits de CDN
Listagens de e-commercerevalidate: 60 + on-demandEquilíbrio entre frescor e performance
Dashboards de usuáriodynamic = 'force-dynamic'Específico do usuário, precisa de dados frescos
Rotas de API (públicas)revalidate: 300 + cache tagsReduzir carga do backend
Rotas de API (auth)cache: 'no-store'Segurança e frescor
Dados em tempo realFetching client-sideCache do servidor inadequado

Mensagens de Erro Comuns e Soluções

"DYNAMIC_SERVER_USAGE"

Error: Dynamic server usage: cookies

Solução: Você está usando funções dinâmicas em uma rota de exports estáticos. Você pode:

  • Remover a função dinâmica
  • Adicionar export const dynamic = 'force-dynamic'
  • Mover lógica dinâmica para componentes cliente

"Invariant: static generation store missing"

Isso geralmente indica tentativa de usar APIs dinâmicas durante o build:

// ❌ Problema export async function generateStaticParams() { const cookieStore = await cookies(); // Não pode usar aqui! return []; } // ✅ Solução: só usar dados estáticos em generateStaticParams export async function generateStaticParams() { const products = await fetch('/api/products', { cache: 'force-cache' }).then(r => r.json()); return products.map(p => ({ id: p.id })); }

Cache Não Invalida na Vercel

// Certifique-se de estar usando a API de revalidação correta import { revalidatePath, revalidateTag } from 'next/cache'; // revalidatePath invalida todos os dados para um path revalidatePath('/products'); // revalidateTag é mais cirúrgico revalidateTag('products-list'); // Ambos requerem que a requisição se origine do mesmo deploy // Use Webhook do CMS → API Route → função revalidate

Conclusão: Modelo Mental para Cache do Next.js

Depois de digerir tudo isso, aqui está o modelo mental para levar adiante:

  1. Por padrão dinâmico no Next.js 15/16. Comece sem cache e adicione onde for benéfico, ao invés de debugar hits de cache inesperados.

  2. Separe estático e dinâmico. Use limites Suspense e composição de componentes para maximizar conteúdo cacheável.

  3. Cache no nível certo. Não use Full Route Cache quando Data Cache para fetches específicos seria suficiente.

  4. Sempre teste com builds de produção. O servidor de desenvolvimento mente sobre comportamento de cache.

  5. Use cache tags para precisão. São mais manuteníveis que invalidação baseada em path para apps complexas.

  6. Monitore comportamento de cache em produção. Adicione logging, use Vercel Analytics, ou implemente headers de cache customizados.

Cache no Next.js é complexo porque está resolvendo um problema complexo: entregar conteúdo rápido e fresco em escala. Uma vez que você entenda as quatro camadas e suas interações, você poderá construir aplicações que são incrivelmente rápidas e confiavelmente atualizadas.

A chave não é lutar contra o cache, mas trabalhar com ele—configurando cada camada apropriadamente para seu caso de uso específico e entendendo que às vezes, a melhor configuração de cache é não ter cache nenhum.

nextjscachingapp-routerreactweb-performancetroubleshooting

Explore ferramentas relacionadas

Experimente estas ferramentas gratuitas do Pockit