Environment Variables: The Right Way to Handle Secrets in Your App
How to manage environment variables for local development, CI/CD, and production without leaking secrets, hardcoding values, or debugging .env file issues for hours.
A surprising number of production incidents trace back to environment variable mishaps — a key hardcoded in source, a .env committed to a public repo, a missing variable that caused a silent failure. Here's the pattern that avoids all of them.
The File Hierarchy (Stop Guessing)
- .env.example — committed, shows required variables with placeholder values and comments. The documentation.
- .env — not committed, local defaults. Sometimes committed for truly non-secret defaults.
- .env.local — not committed, overrides everything for local development.
- .env.production / .env.staging — not committed locally, values set in hosting platform.
Validate at Startup
The worst outcome: a missing environment variable causes a silent failure or a cryptic error deep in runtime. Validate required variables when the application starts and fail loudly if any are missing:
config.ts
const required = [
'DATABASE_URL',
'JWT_SECRET',
'STRIPE_SECRET_KEY',
];
for (const key of required) {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
export const config = {
databaseUrl: process.env.DATABASE_URL!,
jwtSecret: process.env.JWT_SECRET!,
stripeKey: process.env.STRIPE_SECRET_KEY!,
};Client-Side vs Server-Side Variables
In Next.js: only variables prefixed with NEXT_PUBLIC_ are available in the browser. Everything else is server-only. This is critical for secrets — a STRIPE_SECRET_KEY without the NEXT_PUBLIC_ prefix never reaches the client. In Vite: the prefix is VITE_. In Create React App: REACT_APP_. The pattern prevents accidentally exposing secrets by including them in client-side bundles.
The Secret That Got Committed: What To Do
- 1Rotate the compromised secret immediately — assume it's already been harvested.
- 2Remove from git history: git filter-branch or BFG Repo Cleaner (better tool).
- 3Force-push the rewritten history.
- 4Add to .gitignore.
- 5If the repo is public: the secret was exposed. Even after removal from history, assume scrapers captured it.
For Teams: Use a Secrets Manager
Doppler and Infisical are the accessible options for most teams. You define secrets once per environment in a web UI, and they inject values into your local development (via a CLI), CI/CD, and production deployments automatically. No more 'ask Alice for the .env file' for new developers. The cost (Doppler starts free) is worth eliminating the coordination overhead and accidental leakage.
Frequently Asked Questions
Should I commit my .env file to git?+
What's the difference between .env and .env.local?+
How should I share .env values with my team?+
How do environment variables work in CI/CD (GitHub Actions, etc.)?+
🔧 Free Tools Used in This Guide
FreeToolKit Team
FreeToolKit Team
We build free browser-based tools and write practical guides without the fluff.
Tags: