Environment Variables: Stop Hardcoding Secrets
API keys in code? Database passwords committed to Git? Here's how to manage environment config properly.
Table of Contents
- The Problem
- The Solution: Environment Variables
- Setup
- Laravel
- Node.js
- Multiple Environments
- Development
- Staging
- Production
- Accessing Config
- Laravel
- Node.js
- Security Best Practices
- 1. Never Commit .env
- 2. Use .env.example
- 3. Rotate Secrets Regularly
- 4. Use Different Keys Per Environment
- Real Incident
- Managing Secrets in Production
- Option 1: Server Environment Variables
- Option 2: Deployment Platform
- Option 3: .env File on Server
- Validation
- Common Mistakes
- Mistake 1: Using env in Code
- Mistake 2: Committing .env
- Mistake 3: Same Secrets Everywhere
- Config Caching
- Organizing Config
- Real Project Setup
- Checking for Exposed Secrets
- GitHub Secret Scanning
- Manual Check
- Tools
- Bottom Line
Environment Variables: Stop Hardcoding Secrets
Seen this?
$apiKey = 'sk_live_abc123xyz789'; // Committed to Git
$dbPassword = 'supersecret123'; // Everyone can see
Your secrets are public. Here's how to fix it.
The Problem
// config/services.php
return [
'paystack' => [
'secret' => 'sk_live_abc123xyz789', // Hardcoded!
],
];
This file is in Git. Anyone with repo access sees your API key.
The Solution: Environment Variables
// .env (NOT in Git)
PAYSTACK_SECRET=sk_live_abc123xyz789
// config/services.php
return [
'paystack' => [
'secret' => env('PAYSTACK_SECRET'),
],
];
Secrets stay out of Git. Each environment has its own values.
Setup
Laravel
Already configured. Just use .env:
APP_NAME=MyApp
APP_ENV=production
APP_DEBUG=false
DB_HOST=127.0.0.1
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret
PAYSTACK_SECRET=sk_live_xxx
MAIL_PASSWORD=xxx
Node.js
npm install dotenv
// .env
DATABASE_URL=postgresql://user:pass@localhost/db
API_KEY=abc123
// app.js
require('dotenv').config();
const apiKey = process.env.API_KEY;
Multiple Environments
Development
# .env.development
APP_ENV=local
APP_DEBUG=true
DB_DATABASE=myapp_dev
PAYSTACK_SECRET=sk_test_xxx
Staging
# .env.staging
APP_ENV=staging
APP_DEBUG=false
DB_DATABASE=myapp_staging
PAYSTACK_SECRET=sk_test_xxx
Production
# .env.production
APP_ENV=production
APP_DEBUG=false
DB_DATABASE=myapp_prod
PAYSTACK_SECRET=sk_live_xxx
Accessing Config
Laravel
// Get config value
$secret = config('services.paystack.secret');
// With default
$timeout = config('services.api.timeout', 30);
// Check environment
if (app()->environment('production')) {
// Production-specific code
}
Node.js
const apiKey = process.env.API_KEY;
const dbUrl = process.env.DATABASE_URL || 'postgresql://localhost/db';
if (process.env.NODE_ENV === 'production') {
// Production code
}
Security Best Practices
1. Never Commit .env
# .gitignore
.env
.env.*
!.env.example
2. Use .env.example
# .env.example (committed to Git)
APP_NAME=
APP_ENV=
DB_HOST=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
PAYSTACK_SECRET=
Shows what variables are needed without exposing values.
3. Rotate Secrets Regularly
# Generate new app key
php artisan key:generate
# Update API keys quarterly
# Update database passwords annually
4. Use Different Keys Per Environment
# Development
PAYSTACK_SECRET=sk_test_xxx
# Production
PAYSTACK_SECRET=sk_live_yyy
If dev key leaks, production is safe.
Real Incident
Client: E-commerce site
What happened:
- Developer committed
.envto Git - Repo was public
- Someone found Paystack live key
- Made test transactions
- ₦50,000 lost before we noticed
Fix:
- Rotated all API keys
- Made repo private
- Added
.envto.gitignore - Set up secret scanning
Lesson: Never commit secrets. Ever.
Managing Secrets in Production
Option 1: Server Environment Variables
# Set on server
export DB_PASSWORD=secret
export PAYSTACK_SECRET=sk_live_xxx
Option 2: Deployment Platform
Vercel:
vercel env add PAYSTACK_SECRET
Heroku:
heroku config:set PAYSTACK_SECRET=sk_live_xxx
AWS: Use AWS Secrets Manager or Parameter Store.
Option 3: .env File on Server
# Upload .env via SSH
scp .env.production server:/var/www/app/.env
Keep .env out of Git, upload manually.
Validation
Validate required variables on startup:
// Laravel: config/app.php
$required = [
'APP_KEY',
'DB_PASSWORD',
'PAYSTACK_SECRET',
];
foreach ($required as $var) {
if (empty(env($var))) {
throw new Exception("Missing required environment variable: {$var}");
}
}
App won't start if config is missing.
Common Mistakes
Mistake 1: Using env() in Code
// Bad: Doesn't work with config cache
$secret = env('PAYSTACK_SECRET');
// Good: Use config()
$secret = config('services.paystack.secret');
Mistake 2: Committing .env
# Check if .env is tracked
git ls-files | grep .env
# If found, remove it
git rm --cached .env
git commit -m "Remove .env from Git"
Mistake 3: Same Secrets Everywhere
# Don't use production secrets in development
# Use test keys for development
Config Caching (Laravel)
Production optimization:
# Cache config (faster loading)
php artisan config:cache
# After changing .env
php artisan config:clear
php artisan config:cache
Important: env() doesn't work with cached config. Always use config().
Organizing Config
// config/services.php
return [
'paystack' => [
'public_key' => env('PAYSTACK_PUBLIC_KEY'),
'secret_key' => env('PAYSTACK_SECRET_KEY'),
'webhook_url' => env('PAYSTACK_WEBHOOK_URL'),
],
'flutterwave' => [
'public_key' => env('FLUTTERWAVE_PUBLIC_KEY'),
'secret_key' => env('FLUTTERWAVE_SECRET_KEY'),
],
];
Group related config together.
Real Project Setup
Project: Payment processing API
.env.example:
# Application
APP_NAME=PaymentAPI
APP_ENV=production
APP_DEBUG=false
APP_URL=https://api.example.com
# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
# Payment Gateways
PAYSTACK_PUBLIC_KEY=
PAYSTACK_SECRET_KEY=
FLUTTERWAVE_PUBLIC_KEY=
FLUTTERWAVE_SECRET_KEY=
# Email
MAIL_MAILER=smtp
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
# Redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=
REDIS_PORT=6379
Clear. Organized. No secrets exposed.
Checking for Exposed Secrets
GitHub Secret Scanning
GitHub automatically scans for exposed secrets. Enable it.
Manual Check
# Search for potential secrets in Git history
git log -p | grep -i "password\|secret\|key"
Tools
# Install gitleaks
brew install gitleaks
# Scan repo
gitleaks detect
Bottom Line
Environment variables keep secrets safe.
Use .env for local development. Use platform secrets for production. Never commit secrets to Git.
Simple rule: If it's secret, it goes in .env.
Need security audit?
We audit codebases for Nigerian businesses. Find exposed secrets, security issues, vulnerabilities.
📞 WhatsApp: +234 708 711 0468
📧 info@www.raspibtech.com
📍 Lagos Island
Related:
Need Help with Your Project?
Let's discuss how Raspib Technology can help transform your business
Related Articles
How to Migrate an RDS Database Between AWS Accounts
Moving your PostgreSQL or MySQL database to a new AWS account? Here's the complete snapshot-based migration process, including encrypted snapshots, KMS key sharing, and cleanup to stop charges.
Read more →How to Migrate S3 Buckets Between AWS Accounts in 5 Minutes
Moving to a new AWS account? Here's how to migrate your S3 bucket data without losing a single file. Simple, fast, and free.
Read more →AWS Spot Instances: Run Your Staging Server for $2/Month
Stop paying $50+/month for staging servers. Here's how to set up a complete staging environment with AWS Spot Instances, auto-deploy via GitHub Actions, and free SSL.
Read more →