
Table of Contents
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.
.env files for local development, inject environment variables via your deployment platform for staging/production, and integrate AWS Secrets Manager or HashiCorp Vault for dynamic credential rotation and audit trails in high-compliance 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.
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
gitleaksortrufflehogas 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
sopsorgit-cryptto encrypt sensitive files. Only the CI runner with the decryption key can read them. - Restrict file permissions: On production servers, set
.env(if used) to600owned 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.
| Criteria | .env Files | Environment Variables | AWS Secrets Manager | HashiCorp Vault |
|---|---|---|---|---|
| Setup Complexity | Low | Low | Medium | High |
| Rotation Support | Manual | Manual (restart) | Automatic | Dynamic / Automatic |
| Audit Trail | None | None | CloudTrail | Built-in Audit Log |
| Performance Impact | Disk I/O (cached) | Negligible | Network (needs cache) | Network (needs cache) |
| Compliance Ready | No | Partial | Yes (SOC 2 / HIPAA) | Yes (All major) |
| Multi-Cloud | Yes | Yes | No | Yes |
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.