Laravel

Secrets and Config Validation in Laravel

How to validate that all required environment variables are present when your Laravel application starts, preventing silent configuration failures later.

One of the most common production failures is a missing or misconfigured environment variable. The application boots without error, but a job fails hours later because STRIPE_SECRET is empty. Catching this at startup, not at runtime, is far less painful.

The Problem with Silent Defaults

Laravel's env() helper returns null if a variable is not set. This silently propagates through your code:

// config/services.php
'stripe' => [
    'secret' => env('STRIPE_SECRET'), // null if not set
],
// PaymentService.php
$stripe = new Stripe(config('services.stripe.secret')); // Stripe SDK called with null

The SDK will likely throw an authentication error the first time a payment is attempted — not at startup.

Validating Config in a Service Provider

Validate required configuration values early by asserting they are present in a service provider:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Config;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->validateConfig();
    }

    private function validateConfig(): void
    {
        $required = [
            'services.stripe.secret',
            'services.mailgun.secret',
            'queue.connections.redis.host',
        ];

        foreach ($required as $key) {
            if (empty(Config::get($key))) {
                throw new \RuntimeException("Missing required configuration: [{$key}]");
            }
        }
    }
}

This throws an exception during the boot phase, causing the application to fail immediately rather than silently.

Using a Dedicated Package

The spatie/laravel-missing-page-redirector and similar packages exist, but for config validation specifically, based/laravel-cloak and worksome/envy are popular choices. A simpler approach is a custom Artisan command:

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;

class ValidateConfig extends Command
{
    protected $signature = 'config:validate';
    protected $description = 'Validate that all required configuration values are set';

    public function handle(): int
    {
        $required = [
            'APP_KEY'         => config('app.key'),
            'DB_PASSWORD'     => config('database.connections.mysql.password'),
            'STRIPE_SECRET'   => config('services.stripe.secret'),
            'MAIL_USERNAME'   => config('mail.mailers.smtp.username'),
        ];

        $missing = array_filter($required, fn ($v) => empty($v));

        if (!empty($missing)) {
            $this->error('Missing required configuration:');
            foreach (array_keys($missing) as $key) {
                $this->line("  - {$key}");
            }
            return self::FAILURE;
        }

        $this->info('All required configuration is present.');
        return self::SUCCESS;
    }
}

Run this in your CI pipeline before deploying:

- name: Validate config
  run: php artisan config:validate

Never Call env() Outside Config Files

Calling env() directly in application code bypasses the config cache. When you run php artisan config:cache, all env values are compiled into a cache file and env() returns null for everything. Always access environment variables through config values:

// ✅ Correct
$secret = config('services.stripe.secret');

// ❌ Wrong — returns null after config:cache
$secret = env('STRIPE_SECRET');

Sensitive Secrets in Production

  • Never commit .env files to version control. Use .env.example as the template.
  • Use your hosting platform's secret management for production values (Forge environment variables, AWS Secrets Manager, GitHub Actions secrets, Cloudflare Pages environment variables).
  • Rotate secrets when team members leave or when a breach is suspected.
  • Use different secrets per environment — your production Stripe key should never appear in a local .env.

Tips

  • Add a config:validate command to your deployment pipeline so misconfigured environments are caught before traffic hits the new release.
  • Document every required environment variable in .env.example with a comment explaining what it does.
  • Use null coalescing with sensible defaults only for truly optional config, not for credentials.
← Older
Security Headers in Laravel
Newer →
Scheduled Tasks in Laravel

Newsletter

A weekly newsletter on React, Next.js, AI-assisted development, and engineering. No spam, unsubscribe any time.