Why Your NEXT_PUBLIC_ Variables Might Be Exposing Secrets

Published December 28, 2025 ยท 7 min read

Why Your NEXT_PUBLIC_ Variables Might Be Exposing Secrets

If you're using Next.js, any environment variable prefixed with \NEXT_PUBLIC_\ gets bundled directly into your client-side JavaScript. This is by design, but many developers misunderstand what this means: those values are visible to anyone who views your page source.

How NEXT_PUBLIC_ Works

Next.js has two types of environment variables:

Server-side only (safe):

\\\bash

# These stay on the server - never sent to browser

DATABASE_URL="postgres://..."

STRIPE_SECRET_KEY="sk_live_..."

API_SECRET="..."

\\\

Client-side accessible (exposed):

\\\bash

# These are bundled into JavaScript - visible to everyone

NEXT_PUBLIC_API_KEY="..."

NEXT_PUBLIC_STRIPE_KEY="..."

NEXT_PUBLIC_ANALYTICS_ID="..."

\\\

At build time, Next.js replaces all references to \process.env.NEXT_PUBLIC_*\ with the actual values. This means the literal string ends up in your JavaScript bundle.

What Gets Exposed

Open DevTools on any Next.js site and search the JavaScript files. You might find:

\\\javascript

// In the bundled JavaScript

const apiKey = "sk-abc123...";

const config = { apiKey: "AKIA..." };

\\\

Common mistakes developers make:

Variable NameRisk LevelProblem
NEXT_PUBLIC_API_KEYHighGeneric name, might be a secret key
NEXT_PUBLIC_OPENAI_KEYCriticalOpenAI keys should never be client-side
NEXT_PUBLIC_DATABASE_URLCriticalDatabase credentials exposed
NEXT_PUBLIC_STRIPE_SECRETCriticalStripe secret keys allow full account access
NEXT_PUBLIC_SUPABASE_ANON_KEYLowActually designed for client-side use
NEXT_PUBLIC_GOOGLE_MAPS_KEYLowMeant for browser, but should have restrictions

How to Check Your Site

Method 1: View Source
  • Open your production site
  • View Page Source (Ctrl+U or Cmd+U)
  • Search for your domain name or "NEXT_PUBLIC"
  • Look for anything that resembles API keys
  • Method 2: DevTools Network Tab
  • Open DevTools > Network
  • Filter by JS files
  • Click on chunk files (usually named like \[hash].js\)
  • Search for patterns: \sk_\, \sk-\, \AKIA\, \api_key\
  • Method 3: Automated Scan

    Use DomainOptic's Security Audit to scan your JavaScript bundles for exposed secrets automatically.

    The Fix: Move Secrets Server-Side

    Before (insecure):

    \\\javascript

    // pages/api/chat.js or app component

    const response = await fetch('https://api.openai.com/v1/chat/completions', {

    headers: {

    'Authorization': \Bearer \${process.env.NEXT_PUBLIC_OPENAI_KEY}\

    Run a security audit