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.envexclusively on the server. Accessing these in client code returnsundefinedbecause 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:
.env.production(or.env.developmentin dev mode).env.local(loaded in development/test, ignored in production builds).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 returnundefinedin browsers. - Public variables require the
NEXT_PUBLIC_prefix and are inlined at build time, making them available on iOS and other clients. .env.localis ignored in production builds; use.env.productionfor values needed in shipped bundles.- iOS Safari caching can persist stale environment values; clear cache after rebuilds.
loadEnvConfiginpackages/next-env/index.tscontrols 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:
curl -s https://instagit.com/install.md