Fix Vite Environment Variables Undefined in Production Builds

Topic: vite-env-variable-undefined-productionUpdated 7/24/2026

Quick Answer

  • Root cause: Vite only exposes environment variables prefixed with VITE_ to client-side code, and production builds require variables to be defined in .env.production (or the mode-specific .env file) — not just in .env.development.
  • First checks: Verify your variable names start with VITE_ (e.g., VITE_API_URL), confirm .env.production exists in the project root, and ensure no extra spaces or quotes in the file.
  • Minimal fix: Create a .env.production file with all required VITE_ variables, delete the dist folder and node_modules/.vite cache, then rebuild with vite build.
  • Environment boundary: This issue affects all Vite-based projects (React, Vue, Svelte, etc.) when switching from vite dev to vite build. Variables are statically replaced at build time, not at runtime.

What Problem It Solves

When you run vite dev, your environment variables from .env.development (or .env) are available via import.meta.env. But after running vite build and deploying the production output, those same variables may return undefined or empty strings. This happens because Vite's production build uses a different mode (production by default) and only loads the corresponding .env.production file. Without explicit configuration, variables defined only in development-mode files are absent in the production bundle.

Root Cause Analysis

Vite loads environment variables based on the current mode:

  • vite dev defaults to mode development, loading .env.development (plus .env and .env.local).
  • vite build defaults to mode production, loading .env.production (plus .env and .env.local).

If you defined VITE_API_URL only in .env.development, it will be available during development but missing in the production build. Additionally, Vite strips any variable that does not start with VITE_ from client-side code — these are only available in server-side contexts via process.env.

Another common cause is stale build cache. Vite caches transformed modules in node_modules/.vite. If you change .env files without clearing this cache, the old variable values may persist.

Minimal Working Configuration

Project root structure:

my-project/
├── .env                  # loaded in all modes (lowest priority)
├── .env.local            # loaded in all modes, git-ignored
├── .env.development      # loaded only in development mode
├── .env.production       # loaded only in production mode
├── .env.production.local # loaded only in production mode, git-ignored
├── vite.config.ts
└── src/
    └── ...

Example .env.production:

VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App (Production)

Example usage in code:

JAVASCRIPT
const apiUrl = import.meta.env.VITE_API_URL;
console.log(import.meta.env.VITE_APP_TITLE);

Common Errors and Fixes

ErrorLikely CauseFix
import.meta.env returns undefinedVariable not prefixed with VITE_, or missing from .env.productionRename to VITE_* and add to .env.production
Variable value is empty string or literal "undefined"Incorrect .env file format (spaces, quotes, encoding)Use VITE_KEY=value without extra spaces; wrap values with special characters in double quotes
Old variable values persist after changeBuild cache not clearedDelete dist/ and node_modules/.vite/, then rebuild
Cannot read VITE_ variable in Node.js backendVITE_ variables are client-side onlyUse process.env with dotenv for server-side code

Production Notes and Security Checks

  • Never store secrets in VITE_ variables. Any value prefixed with VITE_ is embedded in the client-side JavaScript bundle and visible to anyone who inspects the source code. This includes API keys, database credentials, and private tokens.
  • Use server-side proxies for sensitive operations. Have your backend expose an API endpoint that the frontend calls; the backend handles the actual sensitive request.
  • Runtime configuration is not possible with import.meta.env. If you need to change values after deployment (e.g., different API URLs per tenant), fetch configuration from a server endpoint at runtime instead.
  • CI/CD pipelines should ensure the correct .env.production file is present before running vite build, and that the build directory is cleaned between deployments to avoid stale cache.

FAQ

Q: Why do my VITE_ variables work in development but become undefined in production?

A: The most common reason is that the variables are defined only in .env.development (or .env) but not in .env.production. Vite loads mode-specific files during build. Create .env.production in your project root with all required VITE_ variables. Also check that the file name is exactly .env.production (not .env.prod or .env.production.local without the base .env.production file).

Q: Can I change VITE_ variables at runtime after the build?

A: No. Vite statically replaces import.meta.env.VITE_* with the actual values during the build step. After vite build completes, the values are hardcoded in the output files. To support runtime configuration, fetch settings from a server endpoint on application startup, or use server-side rendering (SSR) to inject variables into the HTML response.

Q: How do I safely handle API keys in a Vite production build?

A: Do not put sensitive keys in VITE_ variables. Instead, implement a backend API proxy: your frontend calls your own server endpoint, and that server holds the actual keys and makes the external API call. If you must expose a key (e.g., a public Firebase API key), ensure it is designed to be public and implement additional security like domain restrictions and rate limiting on the service provider side.

Related Guides