Why Are My Next.js Environment Variables Undefined in Next.js 10.0.5?

In Next.js 10.0.5 and later, environment variables return undefined in browser code—including iOS Safari—unless they are prefixed with NEXT_PUBLIC_, because the framework loads .env files exclusively on the server side during build time via loadEnvConfig.

Next.js 10.0.5 solidified the modern environment variable architecture using the @next/env package, creating a strict boundary between server and client configuration. According to the vercel/next.js source code, the framework deliberately restricts environment access to Node.js processes unless variables are explicitly prefixed for public exposure, which causes them to be statically inlined into the JavaScript bundle.

How Environment Variables Load in Next.js 10.0.5

During server startup and builds, Next.js invokes loadEnvConfig from the @next/env package to populate process.env. In packages/next-env/index.ts, this function reads the appropriate .env* files—such as .env.local, .env.development, or .env.production—and sets values on the server-side process.env object.

In packages/next/src/server/next-server.ts, the framework calls loadEnvConfig during initialization to ensure environment variables are available for server-side rendering. However, this population occurs only within the Node.js process. Values are not automatically transmitted to client browsers unless they carry the NEXT_PUBLIC_ prefix.

Why Variables Return Undefined on iOS and Other Clients

Next.js distinguishes between two distinct types of environment variables:

  • Server-only variables: Remain in process.env exclusively on the server. Accessing these in client code returns undefined because they are stripped from the browser bundle.
  • Public variables: Prefixed with NEXT_PUBLIC_. These are statically inlined into the JavaScript bundle at build time, making them available in browsers including iOS Safari.

If you access a non-prefixed variable in a component running on an iOS device, the value will be undefined. This becomes particularly noticeable on iOS because Safari aggressively caches JavaScript bundles. If you rebuild with a new environment value but the variable lacks the NEXT_PUBLIC_ prefix, or if you changed a server-only variable, the cached client bundle on the device retains stale or missing data.

Environment File Load Order in Next.js 10.0.5

loadEnvConfig follows a specific precedence when loading files, which affects availability across different environments:

  1. .env.production (or .env.development in dev mode)
  2. .env.local (loaded in development/test, ignored in production builds)
  3. .env (base defaults)

When running next build or next start in production mode, Next.js ignores .env.local. If you define variables only in .env.local expecting them to reach the client, they will be missing in production deployments and on physical iOS devices testing production builds.

Resolving Undefined Environment Variables

Expose Client Variables with NEXT_PUBLIC_

To make a variable accessible in browser code, prefix it with NEXT_PUBLIC_ and ensure it is defined in a file loaded during the build:


# .env.production

NEXT_PUBLIC_API_URL=https://api.example.com
// pages/index.js
export default function Home() {
  // ✅ Available on iOS Safari because it was inlined at build time
  const apiUrl = process.env.NEXT_PUBLIC_API_URL;
  return <p>API URL: {apiUrl}</p>;
}

Access Server Secrets via getServerSideProps

For sensitive data that must never reach the client, keep the variable unprefixed and access it only in server-side code:

// pages/dashboard.tsx
export async function getServerSideProps() {
  // ✅ Runs only on the server in Next.js 10.0.5
  const dbPassword = process.env.DATABASE_PASSWORD;
  return { props: { dbPassword: dbPassword ?? '' } };
}

export default function Dashboard({ dbPassword }: { dbPassword: string }) {
  return <pre>{dbPassword}</pre>;
}

Handle iOS Safari Cache Issues

When testing on iOS devices after rebuilding:

  • Clear Safari's cache or use a private browsing window
  • Ensure the bundle hash changes by modifying actual source code if only server variables changed
  • Redeploy the build to ensure the client bundle contains newly inlined NEXT_PUBLIC_ values

Programmatically Loading Environment Files

You can manually trigger the same loading logic used by Next.js 10.0.5 in custom scripts or tests:

import { loadEnvConfig } from '@next/env';

// Loads the same files Next.js would load for the current directory
const { loadedEnvFiles } = loadEnvConfig(process.cwd(), false);
console.log('Loaded env files:', loadedEnvFiles);
console.log('Custom var:', process.env.NEXT_PUBLIC_CUSTOM);

Summary

  • Server-only variables without NEXT_PUBLIC_ are stripped from client bundles and return undefined in browsers.
  • Public variables require the NEXT_PUBLIC_ prefix and are inlined at build time, making them available on iOS and other clients.
  • .env.local is ignored in production builds; use .env.production for values needed in shipped bundles.
  • iOS Safari caching can persist stale environment values; clear cache after rebuilds.
  • loadEnvConfig in packages/next-env/index.ts controls environment loading during server startup.

Frequently Asked Questions

Why are my environment variables undefined in Next.js 10.0.5 on iOS?

Environment variables are undefined on iOS when they lack the NEXT_PUBLIC_ prefix. Next.js 10.0.5 loads these variables only into the server-side process.env during the build process, and non-public values are never sent to the browser. iOS Safari's aggressive caching can also persist old bundles containing outdated or missing values.

What is the difference between server-only and public environment variables in Next.js?

Server-only variables remain in process.env exclusively on the Node.js server and are accessible in API routes or getServerSideProps. Public variables, prefixed with NEXT_PUBLIC_, are statically replaced with their actual values during the build process and embedded directly into the client-side JavaScript bundle.

How do I access environment variables on the client side in Next.js 10.0.5?

Prefix the variable with NEXT_PUBLIC_ in your .env file, such as NEXT_PUBLIC_API_KEY=secret, then reference it as process.env.NEXT_PUBLIC_API_KEY in your React components. The value must be present during the build command execution to be inlined into the bundle.

Why does clearing Safari cache fix undefined environment variables?

Next.js inlines NEXT_PUBLIC_ variables at build time into the JavaScript bundle. If you rebuild with new values but Safari loads a cached version of the bundle (identified by filename hash), it may display stale or missing environment data. Clearing the cache forces the device to download the latest bundle containing current inlined values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client