Environment Variables and next.config

Intermediate
11 min

Environment Variables and next.config

Every application has values that differ between a laptop, staging and production: database URLs, API keys, the public site address. Next.js loads them from .env files with clear precedence and draws a hard line between server secrets and values exposed to the browser. Alongside that sits next.config.ts. After this lesson you will be able to organise environment files, expose variables safely, validate them at startup, and use the most common configuration options.

The .env File Family

Next.js loads variables from the project root in this order; earlier entries win:

| File | Loaded when | Commit to git? | |---|---|---| | process.env (shell, CI, hosting dashboard) | Always | n/a | | .env.development.local / .env.production.local | Matching NODE_ENV | No | | .env.local | Every environment except test | No | | .env.development / .env.production / .env.test | Matching NODE_ENV | Yes | | .env | Always | Yes |

next dev sets NODE_ENV to development; next build and next start set it to production. Keep defaults in .env, personal overrides and secrets in .env.local, and let the hosting platform inject production values.

bash
# .env NEXT_PUBLIC_SITE_URL=http://localhost:3000 LOG_LEVEL=info # .env.local (ignored by git) DATABASE_URL=postgresql://app:secret@localhost:5432/app SESSION_SECRET=replace-with-a-long-random-string

Server Secrets Versus Public Variables

By default a variable is available only in server code: Server Components, Route Handlers, Server Actions, the proxy and next.config.ts. It is never bundled for the browser. To expose a value to client code, prefix it with NEXT_PUBLIC_:

tsx
"use client"; export function Analytics() { // Inlined at build time into the client bundle const key = process.env.NEXT_PUBLIC_ANALYTICS_KEY; return <script data-key={key} />; }

Inlining has two consequences: the value is fixed when next build runs, so changing it requires a rebuild, and it is visible to anyone who opens the developer tools. Server-side variables are read at request time and can be rotated without a rebuild.

Validating at Startup

A missing variable that surfaces as undefined deep in a request is painful to debug. Validate once with Zod, as in the sample at the top of this lesson, and import env instead of process.env everywhere. The application refuses to start with a clear message if anything is missing, and every consumer gets typed values. Add import "server-only" to the file so it can never reach a client bundle.

Configuring the Framework: next.config.ts

next.config.ts is typed and supports the options below, among many others:

typescript
import type { NextConfig } from "next"; const nextConfig: NextConfig = { reactStrictMode: true, output: "standalone", // self-contained build for Docker images: { remotePatterns: [new URL("https://cdn.example.com/**")], }, async redirects() { return [{ source: "/old-blog/:slug", destination: "/blog/:slug", permanent: true }]; }, async headers() { return [{ source: "/(.*)", headers: [{ key: "X-Content-Type-Options", value: "nosniff" }] }]; }, serverExternalPackages: ["mongoose"], // keep native modules out of the bundler }; export default nextConfig;

| Option | Purpose | |---|---| | images.remotePatterns | Allow-list of hosts next/image may optimise | | redirects() / rewrites() | Static routing rules without code in the proxy | | headers() | Security and caching headers per path pattern | | output | "standalone" for containers, "export" for static hosting | | typedRoutes | Type-checked href values in Link |

Changes to next.config.ts require restarting the dev server.

Common mistakes

  • Committing .env.local; add it to .gitignore (the create-next-app template already does).
  • Reading process.env.X in a client component without the prefix, which silently yields undefined.
Quick Quiz
Question 1 of 3

Which prefix exposes an environment variable to browser code?

Key Takeaways

  • .env holds shared defaults, .env.local holds secrets, and hosting injects production values.
  • Server variables are read at request time; NEXT_PUBLIC_ variables are inlined at build time and public.
  • Validate variables once with Zod and import a typed env object everywhere.
  • next.config.ts configures images, redirects, headers, output mode and more, fully typed.
  • Restart the dev server after editing configuration or environment files.

Next lesson: Database Access with Prisma, Drizzle and Mongoose — connect Server Components and actions to a real database.

Environment Variables and next.config - Next.js | CodeYourCraft | CodeYourCraft