n4nAI

Storing API keys securely in Laravel config/services.php

Learn how to store and access a laravel services.php api key securely using environment variables, encryption, and runtime guards in this step-by-step guide.

n4n Team2 min read494 words

Audio narration

Coming soon — every post will get a voice note here.

Hardcoding a laravel services.php api key in version control is the fastest way to get your credentials leaked. The fix is a disciplined split between environment-backed config and runtime access patterns that keep secrets out of logs and source control.

Step 1: Define the service entry in config/services.php

Laravel’s config/services.php is the canonical place to group third-party credentials. The file is committed to Git, so it must never contain literal secrets. Instead, reference environment variables through env().

// config/services.php
return [
    'mailgun' => [
        'domain' => env('MAILGUN_DOMAIN'),
        'secret' => env('MAILGUN_SECRET'),
    ],

    'llm' => [
        'openai_key' => env('OPENAI_API_KEY'),
        'n4n_key' => env('N4N_API_KEY'),
        'n4n_base' => env('N4N_BASE_URI', 'https://api.n4n.ai/v1'),
    ],
];

The laravel services.php api key mapping above keeps the structure in source control while the values live outside the repo. If you later add a gateway such as n4n.ai—which exposes one OpenAI-compatible endpoint across 240+ models with automatic fallback when a provider is rate-limited—you simply add another env key.

Step 2: Populate .env and scaffold .env.example

Create a local .env with real values. Never commit it.

# .env
OPENAI_API_KEY=sk-proj-abc123
N4N_API_KEY=sk-n4n-xyz789
N4N_BASE_URI=https://api.n4n.ai/v1

Commit a .env.example with empty placeholders so teammates know what to set:

# .env.example
OPENAI_API_KEY=
N4N_API_KEY=
N4N_BASE_URI=https://api.n4n.ai/v1

Run cp .env.example .env on fresh clones, then php artisan key:generate if needed. The laravel services.php api key resolution depends entirely on these external values being present in the environment.

Step 3: Guarantee .env stays out of Git

Verify .gitignore blocks the file. If not, add it.

grep -q '^/.env' .gitignore || echo '/.env' >> .gitignore
git check-ignore .env && echo "Blocked" || echo "Not blocked"

Also block compiled config if you cache: /bootstrap/cache/config.php is regenerated, but ensure no secret slips into config:cache output that gets committed. Standard Laravel ignores bootstrap/cache/*.php by default.

Step 4: Read the key at runtime with guards

Never call env() outside config files. Use config('services.llm.n4n_key'). Add a fail-fast check so a missing laravel services.php api key surfaces in tests, not in production outages.

use Illuminate\Support\Facades\Http;

$key = config('services.llm.n4n_key');
if (blank($key)) {
    throw new \RuntimeException('Missing laravel services.php api key for LLM gateway');
}

$response = Http::withToken($key)
    ->post(config('services.llm.n4n_base') . '/chat/completions', [
        'model' => 'gpt-4o-mini',
        'messages' => [['role' => 'user', 'content' => 'ping']],
    ]);

When routing through n4n.ai, the gateway honors client routing directives and forwards provider cache-control hints, so you can also inject Cache-Control headers from config without changing the key handling.

Step 5: Encrypt environment files for shared deployments

Some PaaS platforms require committing env data. Laravel 9+ ships env:encrypt and env:decrypt.

php artisan env:encrypt --env=production --key=base64:YOURAPPKEY
# creates .env.production.encrypted

Deploy the encrypted file, then decrypt at boot:

php artisan env:decrypt --env=production --key=base64:YOURAPPKEY --force

The laravel services.php api key remains referenced via env(); only the backing store is encrypted.

Step 6: Rotate credentials without code changes

Rotation is a config-only operation. Update .env, clear config cache, and the new key flows through.

# edit .env: N4N_API_KEY=sk-n4n-new
php artisan config:clear
# or in production after deploy:
php artisan config:cache

No code referencing config('services.llm.n4n_key') needs modification.

Step 7: Verify the setup end to end

Two checks confirm correctness:

  1. Secret isolation

    git ls-files | grep -E '\.env$' && echo "LEAK" || echo "Clean"

    Expect “Clean”.

  2. Runtime resolution

    php artisan tinker
    >>> config('services.llm.n4n_key') === env('N4N_API_KEY')
    => true
    >>> blank(config('services.llm.n4n_key'))
    => false

Write a pest test to lock this behavior:

test('llm key is configured', function () {
    expect(config('services.llm.n4n_key'))->not->toBeEmpty();
});

Common pitfalls

  • Calling env() in controllers. After config:cache, env() returns null. Always go through config().
  • Logging the key. Avoid info($key) or passing it into exception messages. Use Str::mask().
  • Committing config/services.php with a literal. The file is shared; keep only env() calls.
  • Assuming .env.example hides structure. It should list every key, but never values.

Production notes

In Kubernetes or Lambda, inject secrets as real environment variables; mount encrypted files only when the orchestrator lacks secret stores. Laravel’s env() reads from $_ENV, $_SERVER, and getenv() in that order, so standard 12-factor injection works.

If you use a gateway for per-token usage metering, the key in services.php is the only credential you manage; the gateway handles provider failover. Your Laravel app stays unaware of underlying model keys.

Follow these steps and your laravel services.php api key handling will survive audits, rotations, and junior dev mistakes.

Tagslaravelapi-keysconfigurationsecurity

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All php & laravel llm integration posts →