Hardcoding API keys and database passwords in wp-config.php — even with restrictive file permissions — creates a single point of secret compromise: anyone with read access to the file system has all credentials. A secrets manager (HashiCorp Vault, Doppler, AWS Secrets Manager, or 1Password Secrets Automation) rotates, audits, and injects secrets at runtime, so your repository and filesystem never contain production credentials.
Problem: WordPress wp-config.php stores API keys, database credentials, and payment gateway secrets in plain text — these leak into version control, server backups, and staging environments where they should not be accessible.
Solution: Use an external secrets manager: integrate with HashiCorp Vault via the Vault PHP SDK, fetch secrets at runtime with $client->read('secret/wordpress/stripe'), and cache them in a short-lived transient. For simpler setups, use Doppler or AWS Secrets Manager with a PHP SDK and store only the authentication token in wp-config.php, not the secrets themselves. Never commit secrets to Git.
The code below shows three patterns: environment variable injection from Doppler via a wp-config.php that reads from $_ENV, a HashiCorp Vault AppRole fetch in a WP-CLI command, and a wp-config.php pattern that pulls secrets from AWS Secrets Manager at bootstrap.
wp_json_encode( [ 'role_id' => $role_id, 'secret_id' => $secret_id ] ),
'headers' => [ 'Content-Type' => 'application/json' ],
'timeout' => 5,
] );
if ( is_wp_error( $login ) ) {
wp_die( 'Vault authentication failed.' );
}
$token = json_decode( wp_remote_retrieve_body( $login ), true )['auth']['client_token'] ?? '';
// Read secret
$secret_response = wp_remote_get( "$vault_addr/v1/secret/data/$path", [
'headers' => [ 'X-Vault-Token' => $token ],
'timeout' => 5,
] );
$value = json_decode( wp_remote_retrieve_body( $secret_response ), true )['data']['data'][ $field ] ?? '';
wp_cache_set( $cache_key, $value, 'vault', 300 ); // cache 5 minutes
return (string) $value;
}
// Usage in wp-config.php (before WordPress loads):
// define( 'DB_PASSWORD', wp_vault_get_secret( 'wordpress/prod', 'db_password' ) );
# ── Doppler: inject secrets as env vars at process start ─────────────────
# Install Doppler CLI
brew install dopplerhq/cli/doppler
# Authenticate
doppler login
doppler setup # link to your Doppler project and config
# Run WP-CLI with Doppler-injected secrets
doppler run -- wp core install --url=https://example.com ...
# Run PHP-FPM with Doppler secrets (systemd service override)
# /etc/systemd/system/php8.2-fpm.service.d/doppler.conf:
# [Service]
# ExecStart=
# ExecStart=/usr/bin/doppler run -- /usr/sbin/php-fpm8.2 --nodaemonize
NOTE: Fetching secrets from Vault or AWS Secrets Manager in wp-config.php on every request adds 5–50ms of network latency — always cache the result in PHP's apcu_store() (within a single FPM process lifecycle) or in the WordPress object cache with a short TTL, and ensure your secrets manager endpoint is in the same VPC/datacenter as the WordPress server to minimise latency.