Every environment variable in your repo is one careless commit away from being public. Vite's env model is conservative on purpose: explicit prefix, layered files, build-time substitution.
The file hierarchy
.env— loaded in all modes. Commit this for non-secret defaults..env.local— loaded in all modes, gitignored. Your local overrides..env.development— loaded only in dev. Commit safe-for-dev values..env.production— loaded only in build/preview..env.[mode].local— gitignored mode-specific overrides.
Vite merges them in order; later files override earlier ones.
The VITE_ prefix
Only env vars starting with VITE_ are inlined into client code via import.meta.env.VITE_X. Everything else stays out of the bundle — even if you reference it. This is a static analysis: the prefix is your safety rail.
Build-time substitution
Vite replaces import.meta.env.VITE_X with the actual string at build time. The variable's value bakes into the JS. There's no runtime env lookup in the browser. Two consequences:
- Changing the value requires a rebuild.
- Secret values intended only for runtime should never use VITE_ prefix — they'd be in the JS, readable by anyone who downloads it.
What about runtime config?
If you need configuration that varies per deployment without rebuilding (multi-tenant, region-specific endpoints), fetch the config at runtime from your backend or inject it into window.__APP_CONFIG__ via the HTML at deploy time. Env vars are for build-time defaults; runtime config is a different problem.
Hey guys, if you're doing the exercise, the instructions are actually a bit misleading based on how Vite works.
The exercise says to use
.env.localand expects the production build to fall back to.env. But in Vite,.env.localoverrides EVERYTHING—both dev and build. So if you use.env.local, your production build will still end up with your localhost URL.To make it work the way the exercise actually wants you to, just do this:
Change the local filename from
.env.localto.env.development.localThis forces the localhost URL to only load during
npm run dev. Then when you runnpm run build, it'll properly switch over to the production URL in your.env.Save yourselves the headache and change the filename!