tutorialNext.jsEnvironment VariablesSecurity

Environment Variables in Next.js: Complete Guide

Environment Variables in Next.js: Complete Guide

Environment variables store configuration that varies between environments—API keys, database URLs, feature flags. Next.js handles them with some important security considerations.


Basic Concept

Environment variables let you configure your app without changing code:

// Instead of

const apiKey = "sk-abc123"; // ❌ Hardcoded

// Use

const apiKey = process.env.API_KEY; // ✅ From environment


.env Files

File Types

FilePurposeGit
.envDefault for all environments❌ Ignore
.env.localLocal overrides❌ Ignore
.env.developmentDevelopment onlyOptional
.env.productionProduction onlyOptional
.env.exampleTemplate for team✅ Commit

Loading Priority

1. .env.local (highest priority)

2. .env.development or .env.production

3. .env (lowest priority)


Server vs Client Variables

Server-Only (Default)

.env.local

DATABASE_URL=postgres://localhost/mydb

API_SECRET=super_secret_key

// Only works in server components, API routes, getServerSideProps

const dbUrl = process.env.DATABASE_URL;

Never exposed to browser. Used for:

  • Database connections
  • API secrets
  • Private keys

Client-Accessible

Prefix with NEXT_PUBLIC_:

.env.local

NEXT_PUBLIC_SITE_URL=https://example.com

NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-XXXXX

// Works everywhere, including browser

const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;

⚠️ These are bundled into your JavaScript and visible to anyone.


When to Use Which

Use Server Variables For:

  • Database credentials
  • API keys with write access
  • Payment processor secrets
  • Email service credentials
  • OAuth client secrets

Use NEXT_PUBLIC_ For:

  • Public API keys (e.g., Google Maps)
  • Analytics IDs
  • Site configuration
  • Feature flags visible in UI
  • Public URLs

Examples

.env.local

Database

DATABASE_URL=postgres://user:pass@localhost:5432/mydb

Authentication

AUTH_SECRET=random-32-character-secret-here

GITHUB_CLIENT_ID=abc123

GITHUB_CLIENT_SECRET=secret123

Email (server-side)

RESEND_API_KEY=re_123456789

Public (client-safe)

NEXT_PUBLIC_SITE_URL=https://mysite.com

NEXT_PUBLIC_GA_ID=G-XXXXXXXX

.env.example

Commit this as a template:

Database

DATABASE_URL=

Authentication

AUTH_SECRET=

GITHUB_CLIENT_ID=

GITHUB_CLIENT_SECRET=

Email

RESEND_API_KEY=

Public

NEXT_PUBLIC_SITE_URL=

NEXT_PUBLIC_GA_ID=


Using in Code

Server Components

// app/page.tsx (Server Component by default)

export default function Page() {

const secret = process.env.API_SECRET; // ✅ Works

return

...
;

}

Client Components

'use client';

export default function Analytics() {

const gaId = process.env.NEXT_PUBLIC_GA_ID; // ✅ Works (NEXT_PUBLIC_)

const secret = process.env.API_SECRET; // ❌ Undefined (not NEXT_PUBLIC_)

return

...
;

}

API Routes

// app/api/users/route.ts

export async function GET() {

const db = process.env.DATABASE_URL; // ✅ Works

// Use db...

}

Config Files

// next.config.ts

const config = {

env: {

customKey: process.env.CUSTOM_KEY, // ✅ Works at build time

},

};

export default config;


Deployment

Vercel

1. Go to Project → Settings → Environment Variables

2. Add each variable

3. Choose environments (Production, Preview, Development)

Variable: DATABASE_URL

Value: postgres://...

Environments: ☑️ Production ☑️ Preview

Netlify

1. Site settings → Environment variables

2. Add key-value pairs

Docker

Dockerfile

ENV DATABASE_URL=postgres://...

Or at runtime

docker run -e DATABASE_URL=postgres://... my-app

Other Platforms

Most platforms have environment variable settings. Check their docs.


TypeScript Typing

Add types for autocomplete and safety:

// env.d.ts (or types/env.d.ts)

declare namespace NodeJS {

interface ProcessEnv {

DATABASE_URL: string;

API_SECRET: string;

NEXT_PUBLIC_SITE_URL: string;

NEXT_PUBLIC_GA_ID?: string; // Optional

}

}

Now TypeScript knows your env vars.


Validation

Check required variables at startup:

// lib/env.ts

function getEnv(key: string): string {

const value = process.env[key];

if (!value) {

throw new Error(Missing environment variable: ${key});

}

return value;

}

export const env = {

DATABASE_URL: getEnv('DATABASE_URL'),

API_SECRET: getEnv('API_SECRET'),

SITE_URL: process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000',

};

Or use a library like zod:

import { z } from 'zod';

const envSchema = z.object({

DATABASE_URL: z.string().url(),

API_SECRET: z.string().min(32),

NODE_ENV: z.enum(['development', 'production', 'test']),

});

export const env = envSchema.parse(process.env);


Security Best Practices

1. Never Commit Secrets

.gitignore

.env

.env.local

.env*.local

2. Use .env.example

Commit a template without values:

.env.example

DATABASE_URL=postgres://localhost/dev

API_KEY=your-api-key-here

3. Rotate Compromised Secrets

If a secret leaks:

1. Generate new secret immediately

2. Update in deployment platform

3. Revoke old secret

4. Investigate how it leaked

4. Minimal Permissions

Give API keys only the permissions they need.

5. Audit Regularly

Periodically review what secrets you have and if they're still needed.


Common Issues

"process.env is undefined"

Client-side without NEXT_PUBLIC_ prefix:

// ❌ Won't work in browser

const secret = process.env.SECRET;

// ✅ Works in browser

const publicKey = process.env.NEXT_PUBLIC_KEY;

"Values not updating"

Restart the dev server after changing .env files:

Ctrl+C then:

npm run dev

"Works locally, fails in production"

Make sure variables are set in your deployment platform, not just .env.local.

"Variable is empty string"

Check for typos, missing values, or wrong environment selection in deployment.


Quick Reference

ScenarioUseAvailable Where
Database URLDATABASE_URLServer only
API SecretAPI_SECRETServer only
Public site URLNEXT_PUBLIC_SITE_URLEverywhere
Analytics IDNEXT_PUBLIC_GA_IDEverywhere

Conclusion

Environment variables in Next.js:

1. Server variables - Default, secure, never exposed

2. NEXT_PUBLIC_* - Client-accessible, for public config

3. .env.local - Local development (gitignored)

4. .env.example - Template for team (committed)

5. Platform settings - Production values

Keep secrets secret. Use NEXT_PUBLIC_ only when truly needed. Validate on startup.

Related guides:

Learn more about Next.js development →

Share:

Related Articles

View all

Ready to Migrate Your WordPress Site?

Use our free tool to export your WordPress content in minutes.

Start Free Migration