Next.js Environment Variables Explained

Next.js reads .env files from the project root into process.env. Variables are server-only by default. Prefix a name with NEXT_PUBLIC_ and Next.js copies its value into the browser JavaScript during next build, so it is public and frozen until you rebuild. Never put a secret behind that prefix.

Load order: the first file that defines a variable wins
OrderSourceNotes
1process.envValues already set by the shell or host always win
2.env.$(NODE_ENV).localFor example .env.development.local
3.env.localSkipped when NODE_ENV is test
4.env.$(NODE_ENV)For example .env.production
5.envShared defaults for every environment

Updated

Paste the contents of a .env file below. The checker sorts each variable into server only or sent to the browser, and flags public names that look like secrets. It runs entirely on this page; nothing you paste is uploaded.

.env checker

Paste .env lines

# Database
DB_HOST=localhost
DB_PASS=s3cret
DATABASE_URL=postgres://app:$DB_PASS@$DB_HOST/app

# Public
NEXT_PUBLIC_SITE_URL=https://example.com
NEXT_PUBLIC_STRIPE_SECRET_KEY=sk_test_123

3 server only, 2 sent to the browser

Where each variable can be read
VariableAvailable inNote
DB_HOSTServer only
DB_PASSServer only
DATABASE_URLServer onlyReferences DB_PASS, DB_HOST, which Next.js expands.
NEXT_PUBLIC_SITE_URLServer and browser (inlined at build)
NEXT_PUBLIC_STRIPE_SECRET_KEYServer and browser (inlined at build)Name looks like a secret. Anything with NEXT_PUBLIC_ is visible to every visitor.

Reading variables in code

Server-only value in a Route Handler

DB_URL has no prefix, so it exists only on the server. Route Handlers and Server Components can read it freely.

// app/api/health/route.ts
export async function GET() {
  const hasDb = Boolean(process.env.DB_URL);
  return Response.json({ database: hasDb ? "configured" : "missing" });
}

Public value in a Client Component

The full literal name is required. At build time Next.js swaps it for the value, so the browser sees a plain string.

"use client";

export default function Footer() {
  return <a href={process.env.NEXT_PUBLIC_SITE_URL}>Home</a>;
}

Read a value per request

Awaiting connection() makes the page render dynamically, so the value comes from the running server instead of the build.

import { connection } from "next/server";

export default async function Page() {
  await connection();
  const region = process.env.APP_REGION ?? "unknown";
  return <p>Served from {region}</p>;
}

Load the same files outside Next.js

loadEnvConfig applies the same load order, which keeps scripts and test setups in step with the app.

// env-config.ts
import { loadEnvConfig } from "@next/env";

loadEnvConfig(process.cwd());

What NEXT_PUBLIC_ really does

The prefix is an instruction to the bundler. During next build, every literal process.env.NEXT_PUBLIC_SOMETHING in your code is replaced with the value from the environment where the build ran, and that text ends up in the JavaScript files anyone can download.

That has two consequences. First, a public variable is never secret, no matter how the file is named. Second, it is frozen: promoting one build from staging to production carries the staging value with it.

Reading values at runtime

Server code can read any variable, public or not. To make sure a value is read per request rather than at build time, render the page dynamically, for example by awaiting connection() from next/server before you read process.env.

The browser cannot read server variables at all. If a Client Component needs a runtime value, fetch it from your own Route Handler or pass it in as a prop from a Server Component.

Guard against leaks

A helper that reads an API key can be imported into a Client Component by accident. Add import 'server-only' at the top of such files and the build fails if a client file pulls them in.

How it works

  1. Paste your .env lines into the box, or edit the sample.
  2. Read the table: each variable is marked server only or sent to the browser.
  3. Act on any warning, such as a NEXT_PUBLIC_ name containing KEY, SECRET or TOKEN.
  4. Rebuild after changing any NEXT_PUBLIC_ value, because the old value is baked into the bundle.

Tips

Frequently asked questions

Why is my environment variable undefined in the browser?

Without the NEXT_PUBLIC_ prefix a variable only exists on the server, and Next.js replaces it with an empty string in client code. Add the prefix only if the value is safe to publish, then build again.

I changed a NEXT_PUBLIC_ value on my host and nothing happened. Why?

Public values are inlined when next build runs. A deployed bundle keeps the old value until you build again with the new one.

Does .env.local work in tests?

No. When NODE_ENV is test, Next.js skips .env.local so every test run gets the same values. Put test defaults in .env.test and personal overrides in .env.test.local.

Can one variable reference another?

Yes. Write $OTHER_NAME inside a value and Next.js expands it. If the value needs a literal dollar sign, escape it as \$.

What is NODE_ENV set to?

If you do not set it, next dev uses development and other commands use production. The allowed values are production, development and test.

Related: Next.js App Router, Next.js API routes, Next.js authentication, Next.js vs Node.js, Deploy a React app.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap