Capítulo 78 de 456
Guia de referência para rodar Next.js fora da Vercel (Node.js server, Docker, next start), cobrindo reverse proxy, cache, variáveis de ambiente, multi-instância e streaming.
NEXT_PUBLIC_ prefix: única forma de expor env var ao browser; é inlined no JS bundle durante next build. Sem o prefixo, a env var só existe no servidor.connection(): importado de next/server, força dynamic rendering e faz process.env.MY_VALUE ser avaliado em runtime (não no build), permitindo uma única imagem Docker promovida entre ambientes com valores diferentes.cacheHandler em next.config.js + cacheMaxMemorySize: 0 permite substituir o cache padrão (disco local por instância) por armazenamento durável (Redis, S3) compartilhado entre pods/containers.generateBuildId: gera um build ID consistente entre containers quando se rebuilda por estágio de ambiente (ex. usando git hash).NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: chave AES (16/24/32 bytes, base64) que deve ser igual entre todas as instâncias multi-servidor, senão Server Functions falham com "Failed to find Server Action".deploymentId: habilita proteção contra version skew em rolling deployments — assets estáticos ganham ?dpl=<id>, navegação client-side envia header x-deployment-id; mismatch dispara hard navigation.refreshTags(): método do cache handler custom chamado antes de cada requisição pra sincronizar invalidação de tags entre instâncias (sem isso, revalidateTag() só invalida a instância que chamou).X-Accel-Buffering: no: header necessário pra desabilitar buffering no nginx e permitir streaming (essencial para PPR funcionar com vantagem de time-to-first-byte).module.exports = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // disable default in-memory caching
}
module.exports = class CacheHandler {
constructor(options) { this.options = options }
async get(key) { /* durable storage */ }
async set(key, data, ctx) { /* durable storage, ctx.tags */ }
async revalidateTag(tags) { /* invalida entradas com a tag */ }
resetRequestCache() {}
}
module.exports = {
async headers() {
return [
{ source: '/:path*{/}?', headers: [{ key: 'X-Accel-Buffering', value: 'no' }] },
]
},
}
| Cache-Control header | Quando |
|---|---|
public, max-age=31536000, immutable | Assets imutáveis (hash no nome), não pode ser sobrescrito |
s-maxage: <revalidate>, stale-while-revalidate | Páginas com ISR |
private, no-cache, no-store, max-age=0, must-revalidate | Páginas dinâmicas (dado específico do usuário), inclui Draft Mode |
deploymentId/chave de encriptação consistente/cache compartilhado: causa "Failed to find Server Action", assets ausentes e navegação inconsistente entre pods.deploymentId e cache (via handler custom com refreshTags()).next/image, Proxy e Cache Components funcionam self-hosted com zero config em next start; Proxy não funciona com static export.after() é totalmente suportado self-hosted, mas requer um drain period de 10-30s no shutdown (SIGINT/SIGTERM) pra callbacks pendentes terminarem.use cache): cache handler custom é a peça de infra que sustenta cache em produção multi-instância.refreshTags().