Your wp-config.php backup is public. Here’s how to fix it.
WordPress runs wp-config.php as PHP, so visitors never see what’s inside. A copy with any other ending — wp-config.php.bak, .old, .save, wp-config.php~ — isn’t PHP to the web server, so it is sent as plain text: the database name, user and password, the eight security keys and salts, and the table prefix.
Where the copies come from
- Manual backups before an edit —
cp wp-config.php wp-config.php.bak— left in place afterwards. - Editors. nano writes
wp-config.php.savewhen it is killed mid-edit; a dropped SSH session is enough. Vim leaves.wp-config.php.swp, and Emacs and others leavewp-config.php~. - Migrations and plugins that copy the file before changing it, and archives such as
backup.zipleft in the web root.
DotenvScan checks /wp-config.php.bak, the most common one, and /wp-config.php itself. The steps below deal with all of them.
If wp-config.php itself is exposed
Normally a request for /wp-config.php returns an empty page, because PHP runs the file — DotenvScan shows that as Runs as PHP. If the scan shows it as Exposed, PHP isn’t handling .php files at that address, and the server is sending the source instead. It usually follows a PHP upgrade or a server move: PHP-FPM stopped, a removed PHP module, or a virtual host missing its PHP handler. Every .php file on the site is being sent as source at that point, not just this one.
Block the file at once with the rule in step 1 below — it refuses wp-config.php as well as its copies — then fix the PHP handler, and carry on from step 2: the database password and the keys and salts are leaked.
1. Delete the copies and block the pattern
List every copy on the server. Keep wp-config.php itself and wp-config-sample.php, which holds no secrets:
find /var/www -type f -name '*wp-config*' ! -name 'wp-config.php' ! -name 'wp-config-sample.php'
Delete what it finds — WordPress only ever reads wp-config.php — then make the web server refuse any future copy. On nginx, above the location ~ \.php$ block:
# wp-config.php and every copy of it (.bak, .old, .save, ~, .swp …)
location ~* /\.?wp-config\. {
deny all;
}
On Apache, in the virtual host or .htaccess:
# wp-config.php and every copy of it (.bak, .old, .save, ~, .swp …)
<FilesMatch "^\.?wp-config\.">
Require all denied
</FilesMatch>
Both rules leave posts whose address merely contains “wp-config” alone. If you’re rebuilding the whole .htaccess, the WPSalt .htaccess generator writes WordPress’s rewrite rules with hardening options; add the rule above to it. For .env files, .git and other backups, add the rules from the main guide too.
2. Change the database password
The leaked password may work in your host’s phpMyAdmin, from another account on a shared server, or anywhere else it was reused. Change it, then update DB_PASSWORD in wp-config.php straight away — the site is down until both match:
-- MySQL 5.7+ / MariaDB 10.2+; use the DB_USER value from wp-config.php
ALTER USER 'wp_user'@'localhost' IDENTIFIED BY 'a-new-long-random-password';
On cPanel, Plesk and similar panels, change it under the panel’s database users instead.
3. Replace the keys and salts
The eight constants — AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY and their four _SALT partners — sign WordPress’s login cookies. Together with database access, they are enough to forge a logged-in session without knowing anyone’s password.
Generate eight fresh values with the WPSalt wp-config salts generator — it runs in your browser, with no request to api.wordpress.org — and paste them over the old block in wp-config.php. With WP-CLI, wp config shuffle-salts does the same. Everyone is signed out, you included; WPSalt explains when and why to change WordPress salts.
4. Look for what an intruder may have changed
Database access is full control of the site. Before you call it done, check for:
- Administrators you didn’t create.
wp user list --role=administrator, or Users in the dashboard. WP Server Guard’s guide to an unknown admin user in WordPress covers what to do if you find one. - Changed settings —
siteurlorhomepointing somewhere else, or registration switched on with Administrator as the default role. - Files you didn’t upload — new plugins, PHP files in
wp-content/uploads, edited theme files. - Whether the copy was downloaded at all — search your access logs.
If you find any of these, the site is compromised, not just exposed. A cleanup that misses one backdoor gets reinfected: see the places WordPress cleanups usually miss, or have WP Server Guard audit and clean the site.
5. Stop it happening again
- Keep backup copies outside the web root, for example in your home directory — never next to the live file.
- Move
wp-config.phpone directory above the WordPress folder. WordPress looks there automatically, as long as that directory isn’t another WordPress install. - If your host runs PHP as the site’s own user, set
wp-config.phpto440or400, as WordPress’s hardening guide recommends. - Work through a WordPress security audit checklist for the rest of the site.
Then scan again. To find every wp-config.php and backup copy across all accounts on a server, run ServerSecretVault on the server itself.