Manage Secrets and Config in PHP Apps

Khimananda Oli 8 min read Programming and Languages
Manage Secrets and Config in PHP Apps

By Khimananda Oli | Last reviewed: August 2026

Hardcoded credentials remain the leading cause of PHP application breaches, yet many teams still commit API keys to Git or store database passwords in plain text configuration files. To properly manage secrets and config in PHP apps, you must decouple sensitive values from your codebase entirely, injecting them at runtime through environment variables or dedicated secret stores. This approach satisfies compliance requirements like SOC 2 and ISO 27001 while preventing catastrophic leaks during routine development workflows.

Local Dev.env FileCI/CD PipelineGitHub Actions / GitLabProduction ServerNginx + PHP-FPMSecret StoreVault / AWS SMSecure Configuration FlowSecrets are injected at runtime, never stored in application code
Secure architecture to manage secrets and config in PHP apps across local, CI/CD, and production environments

How do you manage secrets and config in PHP apps using environment variables?

The foundation of secure PHP configuration is the Twelve-Factor App methodology, which mandates storing config in the environment. In practice, this means your application code should never contain literal passwords, API tokens, or hostnames. Instead, it reads from $_ENV or getenv(). For frameworks like Laravel or Symfony, this is abstracted through a configuration loader that merges environment variables with default values.

Setting up .env files safely for local development

Local development requires a convenient way to load environment variables without polluting your global shell. The vlucas/phpdotenv library (standard in Laravel) handles this by parsing a .env file in your project root. Crucially, this file must be listed in .gitignore to prevent accidental commits.

# .env.example (COMMIT THIS)
APP_ENV=local
APP_DEBUG=true
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_local
DB_USERNAME=root
DB_PASSWORD=secret

# .gitignore
.env
.env.*
!.env.example

A common mistake I see in audits is developers copying .env to .env.production and committing it because they forgot to update their ignore rules. Always use .env.example as a template with dummy values, and document required variables in your README. If you are setting up a fresh server, follow the initial Ubuntu server setup guide to ensure your file permissions prevent unauthorized access to these configuration files before deploying.

Injecting variables in production without .env files

In production, you should avoid .env files entirely. They introduce disk I/O overhead on every request (unless cached) and create a persistent secret artifact on the filesystem. Instead, configure environment variables directly in your web server or process manager.

For Nginx with PHP-FPM, set variables in the pool configuration or FastCGI params:

# /etc/php/8.4/fpm/pool.d/www.conf
env[DB_HOST] = $DB_HOST
env[DB_PASSWORD] = $DB_PASSWORD
env[AWS_SECRET_ACCESS_KEY] = $AWS_SECRET_ACCESS_KEY

If you use systemd to manage PHP workers or queue processes, define them in the service unit file. This keeps secrets out of your web root and allows you to rotate them by restarting the service rather than redeploying code. For teams managing complex deployments, understanding how to handle secrets in CI/CD pipelines safely is essential to prevent leakage during the build phase.

When should you use AWS Secrets Manager or HashiCorp Vault for PHP?

Environment variables solve the "secrets in code" problem but introduce new risks: they are visible in process lists (/proc/<pid>/environ), inherited by child processes, and difficult to rotate without downtime. When your compliance posture (SOC 2, ISO 27001) demands audit trails, automatic rotation, or encryption-at-rest for credentials, you need a dedicated secrets manager.

PHP ApplicationAWS SDK / Vault ClientSecrets Manager1. Request Secret2. Authenticated Fetch3. Return Decrypted Value4. Inject into ConfigCaching Layer (APCu / Redis)Secrets are cached in memory for TTL durationReduces API calls from ~1000/sec to ~1/minPrevents latency spikes and rate limiting
Runtime secret retrieval sequence with caching to manage secrets and config in PHP apps efficiently

Integrating AWS Secrets Manager with PHP

AWS Secrets Manager is ideal if your infrastructure already lives on AWS. It provides KMS-backed encryption, automatic rotation for RDS/MySQL, and fine-grained IAM policies. The key implementation detail for PHP is caching. Fetching a secret over HTTP on every request will destroy your performance and trigger rate limits.

Use the AWS SDK for PHP with a local cache adapter. Here is a practical pattern using APCu for single-server setups or Redis for clustered environments:

<?php
use Aws\SecretsManager\SecretsManagerClient;
use Aws\Exception\AwsException;

function getSecret(string $secretName): string {
    // Check cache first (60 second TTL)
    $cacheKey = 'secret_' . md5($secretName);
    $cached = apcu_fetch($cacheKey, $success);
    if ($success) return $cached;

    $client = new SecretsManagerClient([
        'version' => '2017-10-17',
        'region'  => 'ap-south-1', // Use your region
    ]);

    try {
        $result = $client->getSecretValue(['SecretId' => $secretName]);
        $secret = $result['SecretString'];
        
        // Cache for 60 seconds to reduce API calls
        apcu_store($cacheKey, $secret, 60);
        return $secret;
    } catch (AwsException $e) {
        error_log('Failed to retrieve secret: ' . $e->getMessage());
        throw new RuntimeException('Configuration error');
    }
}

This pattern reduces API calls from thousands per second to roughly one per minute per instance. For teams evaluating cloud providers, the AWS vs Azure vs GCP comparison covers how each platform's native secret manager differs in pricing and integration complexity.

Using HashiCorp Vault for multi-cloud or on-premises

Vault is the standard for platform-agnostic secret management. It supports dynamic secrets (generating short-lived DB credentials on demand), transit encryption, and PKI. For PHP, the hashicorp/vault-php SDK provides a clean interface. Vault shines in hybrid environments where you have servers in Nepal-based data centers alongside AWS workloads, as it provides a unified control plane regardless of underlying infrastructure.

What are the best practices for securing .env files and preventing leaks?

Even with perfect runtime injection, leaks happen during development and CI. Defense-in-depth requires multiple layers of protection beyond just .gitignore.

  • Pre-commit scanning: Install gitleaks or trufflehog as a pre-commit hook. These tools detect high-entropy strings and known secret patterns before they reach your repository. This is non-negotiable for any team handling payments or PII.
  • Encrypt at rest in CI: If you must store configuration in Git (e.g., for GitOps), use sops or git-crypt to encrypt sensitive files. Only the CI runner with the decryption key can read them.
  • Restrict file permissions: On production servers, set .env (if used) to 600 owned by the application user. Never make it world-readable. Follow the Ubuntu file permissions guide to understand ownership models that prevent lateral movement.
  • Audit access: Enable CloudTrail for AWS Secrets Manager or Vault audit logs. You must know who accessed what secret and when. This is the first thing auditors check during SOC 2 reviews.
  • Rotate proactively: Treat every secret as compromised after 90 days. Automate rotation using Lambda functions or Vault's dynamic secret engines. Manual rotation fails because humans forget.

How does Laravel's config caching interact with secret management?

Laravel's config:cache command serializes all configuration into a single PHP array for performance. This creates a critical interaction with secrets: environment variables are only read during the cache build step. If you change a secret in AWS Secrets Manager or update an environment variable without rebuilding the cache, your application continues using the old value.

This behavior is actually beneficial for security—it prevents runtime secret fetching overhead—but requires disciplined deployment. Your deploy script must always run php artisan config:cache after updating environment variables. Never modify .env on a live server expecting immediate effect; it will be ignored if the cache exists.

For dynamic secrets that change frequently (like temporary S3 credentials), bypass Laravel's config system entirely. Fetch them at runtime using a service provider or middleware, and cache them separately using Redis with a TTL shorter than the secret's expiration. This hybrid approach gives you both performance for static config and freshness for volatile credentials.

.env Files✓ Simple setup✓ Framework native✗ Disk exposure✗ No rotation✗ No audit trailBest for:Local developmentSmall projectsEnv Variables✓ No disk artifacts✓ Platform agnostic~ Visible in /proc✗ Restart required✗ No central auditBest for:Containerized appsPaaS deploymentsAWS Secrets Mgr✓ KMS encryption✓ Auto rotation✓ CloudTrail audit✗ AWS lock-in✗ API cost/latencyBest for:AWS-native stacksCompliance workloadsHashiCorp Vault✓ Dynamic secrets✓ Multi-cloud✓ Full audit log✗ Ops complexity✗ Self-host overheadBest for:Hybrid / on-premZero-trust architectures
Decision matrix to help you choose the right method to manage secrets and config in PHP apps based on infrastructure and compliance needs
Criteria.env FilesEnvironment VariablesAWS Secrets ManagerHashiCorp Vault
Setup ComplexityLowLowMediumHigh
Rotation SupportManualManual (restart)AutomaticDynamic / Automatic
Audit TrailNoneNoneCloudTrailBuilt-in Audit Log
Performance ImpactDisk I/O (cached)NegligibleNetwork (needs cache)Network (needs cache)
Compliance ReadyNoPartialYes (SOC 2 / HIPAA)Yes (All major)
Multi-CloudYesYesNoYes

Secure Configuration Management for Production PHP

To effectively manage secrets and config in PHP apps, start by eliminating hardcoded values and adopting environment-based injection today. Move to a dedicated secrets manager when your compliance requirements demand audit trails or when manual rotation becomes a reliability risk. Remember that security is not a feature you add later—it is the foundation upon which trust is built. If your team needs help designing an audit-ready secret management architecture or migrating legacy PHP applications to modern security standards, reach out to discuss your infrastructure.

Frequently Asked Questions

Use vlucas/phpdotenv to parse .env files into $_ENV and getenv(). Never commit .env to version control; add it to .gitignore immediately.

Inject secrets via cloud provider secret managers like AWS Secrets Manager or HashiCorp Vault at runtime, avoiding local files entirely.

Yes, run php artisan config:cache in production to compile all configuration values into a single cached file for faster bootstrap times.

Implement dual-secret support where the application accepts both old and new credentials during a transition window before deprecating the old ones.

Both work well, but Symfony Dotenv integrates natively with Symfony Flex while phpdotenv remains framework-agnostic and widely supported across ecosystems.

Create a dedicated config validation service that checks all critical environment variables exist and match expected formats before processing requests.

No. Never log secrets, tokens, or passwords. Use structured logging with redaction filters to mask sensitive fields automatically in 2026 deployments.

Maintain separate .env.staging and .env.production files loaded conditionally based on APP_ENV, keeping base defaults in .env.example as documentation.

Set 600 permissions so only the web server user can read the file, preventing other system users from accessing sensitive credentials.

Mount Kubernetes Secrets as environment variables or volume files, using External Secrets Operator to sync from cloud secret managers automatically.

Yes, use libsodium or OpenSSL to encrypt secrets at rest and decrypt them in memory during application bootstrap using a master key.

Override $_ENV in PHPUnit setUp methods or use Laravel's built-in testing helpers to set temporary values without modifying actual .env files.

The app should fail fast with a clear error message during bootstrap rather than silently returning null values that cause data corruption later.

Yes, use csharpru/vault-php or jippi/vault-php-sdk to authenticate and fetch secrets dynamically from Vault endpoints in your application.

Review access logs monthly and after every deployment to detect unauthorized reads, unexpected rotation failures, or misconfigured permission boundaries.