Next.js
Variables de entorno y configuración
Explica variables build-time, runtime y públicas, carga de archivos .env, validación, secret management, previews, Docker y configuración de Next.js.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
Next.js
Explica variables build-time, runtime y públicas, carga de archivos .env, validación, secret management, previews, Docker y configuración de Next.js.
La configuración de una aplicación Next.js existe en varias fases: variables cargadas al desarrollar o construir, valores públicos incorporados al bundle y secretos disponibles en el runtime servidor. Un nombre dentro de .env no determina por sí solo cuándo se lee ni si permanece privado; import graph, output y plataforma completan el contrato.
source code
+ .env files
+ platform environment
↓
next dev / next build / runtime
↓
server-only values
or
NEXT_PUBLIC values in client bundleSepara siempre:
Next.js carga archivos desde la raíz del proyecto, incluso cuando utilizas src:
.env
.env.local
.env.development
.env.development.local
.env.production
.env.production.local
.env.test
.env.test.localLos archivos .local normalmente se excluyen de Git. .env puede contener defaults no secretos, pero no debes asumir que un repositorio privado evita filtraciones.
Next.js busca primero process.env y luego archivos específicos del entorno. La configuración proporcionada por CI o hosting normalmente debe dominar los defaults del repositorio.
No dependas de cargar .env manualmente dentro del código de aplicación; Next.js lo coordina. Para scripts externos al runtime de Next.js puedes usar @next/env.
DATABASE_URL="postgresql://..."
AUTH_SECRET="..."import "server-only";
const databaseUrl = process.env.DATABASE_URL;Una variable sin NEXT_PUBLIC_ no se expone automáticamente al bundle. Sin embargo, deja de ser secreta si:
El prefijo no es un mecanismo criptográfico; el diseño de boundaries evita la fuga.
NEXT_PUBLIC_ANALYTICS_ID="site_123"const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID;Next.js reemplaza estas referencias durante next build. El valor queda congelado en el bundle generado.
Consecuencia:
build image once
→ deploy same artifact to staging and production
→ NEXT_PUBLIC value remains the build valueSi necesitas configuración pública runtime, entrégala desde servidor mediante una respuesta, script o endpoint controlado, sabiendo que será visible.
El bundler reconoce referencias estáticas:
process.env.NEXT_PUBLIC_ANALYTICS_IDNo confíes en acceso dinámico para inline:
const key = "NEXT_PUBLIC_ANALYTICS_ID";
process.env[key];Además de fallar optimizaciones, oculta qué variables consume el código.
process.env produce string | undefined. Valida una vez en una frontera server-only:
// src/env/server.ts
import "server-only";
import { z } from "zod";
const schema = z.object({
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string().min(32),
NODE_ENV: z.enum(["development", "test", "production"]),
});
export const serverEnv = schema.parse({
DATABASE_URL: process.env.DATABASE_URL,
AUTH_SECRET: process.env.AUTH_SECRET,
NODE_ENV: process.env.NODE_ENV,
});Configuración pública separada:
const publicSchema = z.object({
NEXT_PUBLIC_ANALYTICS_ID: z.string().min(1),
});No exportes un único objeto que mezcle server y client env.
Una variable obligatoria ausente debe fallar en build o startup, no durante la primera compra de un usuario.
Pero una validación importada durante build puede exigir secretos que solo necesita un job/runtime diferente. Divide schemas por capacidad:
env/auth.ts
env/database.ts
env/email.ts
env/public.tsCada entrypoint valida lo que usa.
Servicios locales, URLs localhost (se abre en otra pestaña) y credenciales de prueba.
Entorno aislado por branch/PR. Nunca debe apuntar a DB de producción por defecto. Puede tener URLs dinámicas y OAuth callbacks distintos.
Secretos reales, dominio canónico, observabilidad y políticas estrictas.
No uses NODE_ENV para distinguir preview de production; muchas plataformas ejecutan ambos como production. Define una variable como APP_ENV o usa metadata confiable de plataforma.
Un .env.local es comodidad local, no un vault.
Para claves de firma, soporta una ventana con clave actual y anterior:
sign with key v2
verify with v2 or v1
→ expire old sessions/tokens
→ remove v1Cambiar bruscamente puede invalidar todas las sesiones. Para una credencial comprometida, esa invalidación puede ser necesaria.
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "cdn.example.com",
pathname: "/products/**",
},
],
},
experimental: {
// only documented, intentional flags
},
};
export default nextConfig;La configuración se evalúa en build/servidor. No exportes secretos mediante la opción histórica env; esos valores pueden insertarse en el bundle. Usa process.env con boundaries.
const isProduction = process.env.APP_ENV === "production";
const nextConfig: NextConfig = {
productionBrowserSourceMaps: false,
logging: {
fetches: {
fullUrl: !isProduction,
},
},
};Mantén diferencias mínimas. Cuantas más ramas, más probable es que preview no represente producción.
En self-hosting, variables privadas pueden leerse al iniciar el contenedor y durante request según el código. En static export no existe servidor Next.js después del build; cualquier configuración necesaria debe quedar incorporada o solicitarse a un servicio externo desde cliente.
En serverless, cada función recibe variables de la plataforma; cambios pueden requerir redeploy/restart según proveedor.
Patrón:
build stage
→ install and next build
runtime stage
→ copy standalone output
→ inject runtime server secrets
→ start serverNo pases secretos como ARG de build si no son necesarios: quedan en layers/history. Usa mounts secretos del builder cuando una dependencia privada exige credenciales.
Los valores NEXT_PUBLIC_ sí deben existir al construir porque quedan inline.
Las variables se cargan desde la raíz de la aplicación Next.js, no necesariamente la raíz del workspace. Herramientas como Turborepo necesitan declarar env inputs para invalidar cache de build:
NEXT_PUBLIC_API_URL changes
→ build cache key must changeNunca almacenes un output construido con valores públicos de otro entorno.
.env.test aporta defaults reproducibles. Los runners pueden no ejecutar exactamente el loader de Next.js; usa loadEnvConfig o configuración del runner.
No uses servicios de producción en tests. Valida que las variables obligatorias estén presentes en CI.
DATABASE_URL
→ server runtime only
AUTH_SECRET
→ server build/runtime modules only
NEXT_PUBLIC_SITE_URL
→ visible and frozen at build
APP_ENV
→ development | preview | productionPara canonicals, una URL pública build-time funciona si cada entorno construye su propio artefacto. Si promueves el mismo artefacto, determina la URL desde request/host validado o una configuración runtime pública.
NEXT_PUBLIC_ la publica.
Builds de tareas que no usan el servicio fallan innecesariamente.
Preview usa optimizaciones de producción.
El bundle de producción conserva staging.
Pueden quedar en layers.
Crean comportamiento dependiente de versión sin necesidad.
Filtra todas las credenciales.
NEXT_PUBLIC_ queda visible y normalmente congelado en build..env vive en la raíz de la app.NEXT_PUBLIC_API_URL?.env.local y un secret manager?NODE_ENV no distingue preview?NODE_ENV=production.NEXT_PUBLIC_; secretos runtime deben inyectarse después cuando sea posible.Metadata y SEO en App Router utiliza configuración canónica y datos de ruta para describir cada documento a buscadores y redes.