TLDR: I once left DB credentials in a public repo and learned the hard way: environment variables are the safest way to store secrets for WordPress when configured correctly. Use server-level environment variables, avoid committing .env files, keep sensitive settings out of wp-config.php, use least privilege, rotate keys, and test recovery. This article walks you through what environment variables are, why they matter for WordPress security, step-by-step hardening techniques, common mistakes to avoid, and practical recovery tips.
Section Heading
I still remember the stomach drop the first time I realized I had pushed a database password into a GitHub repo. It forced me to audit how I handled configuration across staging, production, and local development. Since then I moved to environment-driven configuration for every WordPress project I touch. In this guide I will explain what environment variables are, why they matter for WordPress security, how I set them up in different environments, and the pitfalls you should avoid.
What is an environment variable?
Environment variables are key/value pairs available to processes on a server. Instead of embedding secrets like DB_PASSWORD, API keys, or salts in code or configuration files, you expose them to the runtime environment. In PHP you can read them with getenv or $_ENV, and in WordPress you can reference them inside wp-config.php so your repository never contains the secret values.
Why environment variables matter for WordPress security
You might be asking: what difference does it make if the password is in wp-config.php or in an environment variable? It matters for several reasons:
- Separation of code and secrets reduces leak surface. Your codebase can be public or shared with contractors without exposing credentials.
- Infrastructure orchestration tools like Docker, Kubernetes, and most managed hosts support secrets natively, making automation safer.
- Changing a secret no longer requires touching code or redeploying a repository; you can rotate secrets at the host or container level.
- Environment variables encourage least privilege by making it easier to supply different credentials per environment (local, staging, production).
How I approach environment variables for WordPress — step by step
Let me walk you through the practical setup I use on most projects. This works whether you host on a cloud VM, a platform-as-a-service, or Docker.
1) Identify what belongs in environment variables
Start by listing every secret or environment-specific configuration point in wp-config.php and your plugins. Typical candidates include:
- DB_NAME, DB_USER, DB_PASSWORD, DB_HOST
- AWS or external storage keys, API keys for third-party services
- AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY and their salts
- WP_DEBUG, WP_ENV or custom flags that change behavior between environments
- SMTP credentials
2) Inject variables at the server or orchestration level
Where possible, set environment variables at the host level rather than via a file checked into the repo. Examples:
- On Linux systemd units or Apache/Nginx with PHP-FPM, use process environment variables and secure file permissions.
- On Docker, use docker secrets for production or docker-compose env_file for local dev, but never commit env_file with secrets.
- On Kubernetes, store secrets in Kubernetes Secret objects and consume them as environment variables or mounted files with restricted access.
- On managed hosts (e.g., WP Engine, Flywheel, or similar), use the platform UI to set environment variables.
3) Read environment variables in wp-config.php safely
In wp-config.php I keep only the logic that reads from the environment and validates required values. Example pattern I use (paraphrased):
- Call getenv(‘DB_PASSWORD’) or use $_ENV with fallback.
- Fail fast if a required variable is missing in non-local environments, logging a clear error but never echoing secrets.
- Use defined fallbacks for local development only, and keep that file out of version control.
4) Protect .env files and local development secrets
I use a .env file for local convenience but add it to .gitignore. Treat the .env file like a secrets vault on your machine: use OS-level encryption where possible and avoid uploading to cloud storage. When pairing or sharing, use secure secret-sharing tools such as Bitwarden, 1Password, or environment templating solutions that inject values at runtime.
5) Use platform secret management where possible
When my application runs on a host that offers secrets management or integrated key management services, I prefer that over plain environment variables. These platforms can rotate keys, audit access, and keep secrets out of process memory where possible. Examples include AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, or a managed host secret store.
6) Enforce least privilege
I configure database users with minimal privileges. A WordPress DB user rarely needs DROP or superuser rights. Lock down permissions to only what the application needs. That reduces blast radius even if a credential is compromised.
7) Rotate keys and automate updates
I set a rotation policy and automate it where possible. For API keys and DB passwords, schedule rotation and test application behavior during rotation windows. Use credential versioning and blue/green deployments when changing a secret so the site stays online and you can roll back quickly if something breaks.
8) Audit and logging
Log access to secret stores and environment changes. Audit who updated what and when. Keep logs out of the public webroot, and ensure logs themselves do not contain the plain-text values of secrets. Use hashed or redacted logging for any debug output that touches sensitive keys.
Common things to avoid
Here are the mistakes that used to trip me up and how I avoid them now:
- Committing .env or wp-config.php with credentials to Git repositories. Use pre-commit hooks to prevent accidental commits and scan your history with secret scanning tools.
- Relying on WP_DEBUG to diagnose issues in production. It can expose paths and sometimes error messages that help attackers. Keep debug off in production and log to secure destinations.
- Using a single credential across environments. Use distinct credentials per environment so a local machine compromise does not affect production.
- Giving the DB user full privileges. Grant only what WordPress needs.
- Storing secrets in world-readable files or on shared network drives without encryption.
How to recover if a secret is leaked
If you detect a leaked key, act fast:
- Rotate the leaked credential immediately. Create new keys and update the environment without committing them to the repo.
- Revoke backups of the leaked key and audit where that key was used.
- Invalidate sessions if the leak could allow login (for example, compromised AUTH_KEY salts). Replace salts and force users to reauthenticate.
- Scan your repository history for leaked values and purge them using git filter-branch or the BFG Repo-Cleaner, then rotate the secrets again.
Real-world examples and hosting specifics
Different hosts present different options. On a DigitalOcean Droplet I export variables in the systemd unit running PHP-FPM. On Docker, I mount secrets to /run/secrets and read them from wp-config.php. On managed WordPress hosts, I use the environment variable UI to set production-only variables. The core principle remains the same: keep secrets out of the repository and accessible only to the runtime.
Plugin considerations and third-party integrations
Plugins sometimes require API keys or OAuth credentials. Never paste these into plugin settings if the plugin stores them in the database in plaintext. Prefer plugins that support retrieving keys from environment variables or a secure secret store. If a plugin has no option, consider wrapping it with a small plugin or mu-plugin that fetches the required keys from getenv and supplies them programmatically.
Useful functions and code patterns
I use compact helper functions in my mu-plugins to standardize access to environment configuration. Examples I commonly use:
- env(‘KEY’, ‘default’) wrapper that reads getenv, checks $_ENV, and returns safe defaults.
- require_env(‘KEY’) which throws a controlled fatal if a required production-only value is missing.
- sanitize_env_string to trim and validate values before use in connections or headers.
Operational tips I learned the hard way
- Test secret rotation in staging first. I always verify the app can read the new secret before revoking the old one.
- Document required environment variables in a template file like .env.example, never with the actual secrets.
- Use CI/CD secret injection to keep build pipelines from exposing secrets in logs. Mask secrets in CI logs and never echo them during builds.
- Use short-lived tokens for third-party APIs if the provider supports them. That limits exposure from accidental leaks.
What should you avoid?
To summarize the anti-patterns:
- Embedding secrets in code or configuration committed to version control.
- Storing production secrets in shared, unencrypted cloud storage.
- Leaving detailed error pages enabled on production that disclose environment details.
- Giving overly broad privileges to DB users and API tokens.
Frequently Asked Questions
How do I move existing wp-config secrets into environment variables?
I migrate by creating environment variables on the host, updating wp-config.php to read getenv for each value, and then removing the hard-coded values from the file. Do this during a maintenance window: set the new variables first, verify the site reads them, then remove the inline values. If you need guidance, testing on staging makes this low risk. I also recommend you keep a reset WordPress site plan so you can recover quickly if something goes wrong.
Can I use a .env file in production?
Technically yes, but it is risky. If you must, keep the file permission-restricted, encrypted at rest, and outside webroot. I prefer platform secret stores or OS-level environment variables for production because those integrate with rotation, auditing, and access controls. For local development use a .env file but keep it in .gitignore and share values via secured password managers.
What happens if I change AUTH_KEY or salts?
Changing AUTH_KEY and other salts invalidates existing authentication cookies and forces all users to log in again. That is an intentional security measure and useful if you suspect a compromise. Replace salts through environment variables and announce downtime or scheduled maintenance because user sessions will be reset.
How do I handle plugin keys that store credentials in the database?
Prefer plugins that support reading keys from environment variables. If a plugin stores keys in the database and cannot be changed, store the key encrypted and decrypt just-in-time using a key management system. Alternatively write a small mu-plugin that reads the key from the environment and injects it into the plugin at runtime so it never persists in plaintext in the database.
How can I test that I did not accidentally commit secrets?
Use secret-scanning tools like git-secrets, TruffleHog, or GitHub’s secret scanning. I run scans in CI on every pull request and use pre-commit hooks locally. If you discover a leak, rotate the secret immediately and scrub the history from the git repository.
Do I need to restart PHP-FPM or containers after changing environment variables?
Yes. For process-level environment changes you typically need to restart PHP-FPM, Apache, or the container so the new environment is loaded. In orchestrated environments, redeploying the container with updated secrets is the normal pattern. Remember to sync secrets across any clustered instances to avoid inconsistent behavior.
How do caching and environment variables interact?
Caching layers like object caching or opcode caches may retain values in memory. After rotating secrets, flush caches and restart the relevant services to ensure no stale secrets remain in memory. If you need help with cache procedures, I often run a cache purge as part of rotation and sometimes use a documented purge cache WordPress step in my deployment checklist.
Final checklist before going live
- Remove all hard-coded secrets from the repository and confirm with a scanner.
- Set required environment variables in the production secret store and restrict access.
- Verify application can read variables and fail safely if required values are missing.
- Rotate any credentials you exposed during development and revoke old ones.
- Document the recovery plan in case of leaks and practice it on staging.
As you build and maintain WordPress sites, environment variables will become one of your most powerful tools for reducing risk. They do not eliminate the need for good permissions, secure hosting, and monitoring, but they remove a frequent source of accidental leaks. If you follow the steps above you will make it much harder for attackers to find credentials and much faster for you to respond if something goes wrong. If you want, I can walk through a specific hosting setup you use and provide a tailored checklist.
And finally, if you ever have to move or migrate WordPress site or perform a reset WordPress site, ensure your secret management steps are part of that plan so credentials remain secure during the process.