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@raspibtech.com
📍 Lagos Island
Related:
Need Help with Your Project?
Let's discuss how Raspib Technology can help transform your business
Related Articles
Laravel 11: What Changed and Why You Should Care
Laravel 11 is out. Slimmer structure, better performance, and features that actually save time. Here's what matters.
Read more →Laravel 12: The Upgrade You've Been Waiting For
Laravel 12 brings major improvements. Here's what changed and why it matters for your projects.
Read more →Next.js 15: The Features That Actually Matter
Next.js 15 changed a lot. Here's what affects your projects, what breaks, and when to upgrade.
Read more →