Tools: Latest: Stop Hardcoding Secrets: Environment Variables Best Practices in 2026
The Problem
The Solution: .env Files + Environment Variables
Step 1: Create a .env file
Step 2: Load it in your code
Step 3: NEVER commit .env
Best Practices Checklist
1. Use .env.example (committed) + .env (gitignored)
2. Validate env vars at startup
3. Use different .env files per environment
4. Never log environment variables
5. Rotate secrets regularly
Production: Use a Secrets Manager
Quick Audit: Is Your Repo Safe? I've reviewed hundreds of GitHub repos. The #1 security mistake? Hardcoded secrets. Here's how to handle environment variables properly in 2026. This code gets committed, pushed, and now your database password is public forever (yes, even if you delete the commit — git history remembers). Set a calendar reminder. Rotate API keys and passwords at least every 90 days. For production, .env files aren't enough. Use: Run this in your repo: More developer guides at lucasmdevdev.github.io Templates let you quickly answer FAQs or store snippets for re-use. check out varlock.dev - it's a huge upgrade from default dotenv loading. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse
// DO NOT DO THIS
const db = mysql.connect({ host: 'production-db.example.com', password: 'super_secret_password_123'
});
// DO NOT DO THIS
const db = mysql.connect({ host: 'production-db.example.com', password: 'super_secret_password_123'
});
// DO NOT DO THIS
const db = mysql.connect({ host: 'production-db.example.com', password: 'super_secret_password_123'
});
DATABASE_URL=postgres://user:pass@host:5432/mydb
API_KEY=sk_live_abc123
JWT_SECRET=your-256-bit-secret
NODE_ENV=development
DATABASE_URL=postgres://user:pass@host:5432/mydb
API_KEY=sk_live_abc123
JWT_SECRET=your-256-bit-secret
NODE_ENV=development
DATABASE_URL=postgres://user:pass@host:5432/mydb
API_KEY=sk_live_abc123
JWT_SECRET=your-256-bit-secret
NODE_ENV=development
npm install dotenv
npm install dotenv
npm install dotenv
require('dotenv').config(); const db = mysql.connect({ host: process.env.DATABASE_HOST, password: process.env.DATABASE_PASSWORD
});
require('dotenv').config(); const db = mysql.connect({ host: process.env.DATABASE_HOST, password: process.env.DATABASE_PASSWORD
});
require('dotenv').config(); const db = mysql.connect({ host: process.env.DATABASE_HOST, password: process.env.DATABASE_PASSWORD
});
pip install python-dotenv
pip install python-dotenv
pip install python-dotenv
from dotenv import load_dotenv
import os load_dotenv() db_password = os.getenv('DATABASE_PASSWORD')
from dotenv import load_dotenv
import os load_dotenv() db_password = os.getenv('DATABASE_PASSWORD')
from dotenv import load_dotenv
import os load_dotenv() db_password = os.getenv('DATABASE_PASSWORD')
.env
.env.local
.env.production
.env
.env.local
.env.production
.env
.env.local
.env.production
# .env.example (committed — shows structure, no real values)
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=your-api-key-here
JWT_SECRET=generate-a-random-string
# .env.example (committed — shows structure, no real values)
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=your-api-key-here
JWT_SECRET=generate-a-random-string
# .env.example (committed — shows structure, no real values)
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=your-api-key-here
JWT_SECRET=generate-a-random-string
const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET']; for (const key of required) { if (!process.env[key]) { console.error(`Missing required env var: ${key}`); process.exit(1); }
}
const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET']; for (const key of required) { if (!process.env[key]) { console.error(`Missing required env var: ${key}`); process.exit(1); }
}
const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET']; for (const key of required) { if (!process.env[key]) { console.error(`Missing required env var: ${key}`); process.exit(1); }
}
.env.development
.env.staging
.env.production
.env.development
.env.staging
.env.production
.env.development
.env.staging
.env.production
// NEVER do this
console.log('Config:', process.env); // If you must debug, redact:
console.log('DB connected to:', process.env.DATABASE_HOST);
// NEVER do this
console.log('Config:', process.env); // If you must debug, redact:
console.log('DB connected to:', process.env.DATABASE_HOST);
// NEVER do this
console.log('Config:', process.env); // If you must debug, redact:
console.log('DB connected to:', process.env.DATABASE_HOST);
# Check if .env is in .gitignore
grep -q ".env" .gitignore && echo "OK: .env is gitignored" || echo "WARNING: .env not in .gitignore" # Check for hardcoded secrets (basic scan)
grep -rn "password\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
grep -rn "api.key\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
# Check if .env is in .gitignore
grep -q ".env" .gitignore && echo "OK: .env is gitignored" || echo "WARNING: .env not in .gitignore" # Check for hardcoded secrets (basic scan)
grep -rn "password\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
grep -rn "api.key\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
# Check if .env is in .gitignore
grep -q ".env" .gitignore && echo "OK: .env is gitignored" || echo "WARNING: .env not in .gitignore" # Check for hardcoded secrets (basic scan)
grep -rn "password\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
grep -rn "api.key\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" . - Secrets go in .env, never in code
- .env goes in .gitignore, always
- .env.example gets committed (with placeholder values)
- Validate env vars at startup
- Use a secrets manager in production - Email [email protected]
- Location BC, Canada
- Work Cofounder of DMNO - https://dmno.dev
- Joined Mar 15, 2020