Why wp-config.php hardening matters
This one file contains your database name, user and password, your authentication keys and salts, and settings that change how WordPress behaves. Anyone who can read it can read your database. Anyone who can write it can change how the whole site works.
Protecting it is mostly about three things: who can read the file on the server, whether the web server can ever serve it as text, and what the settings in it allow.
File permissions and ownership
As a starting point, set directories to 755 and files to 644, and make the sensitive file stricter. The file should be owned by the account that runs your site. Never make anything 777, which lets any process on the server change it.
On a typical shared host, 640 or 600 works for wp-config.php. On a server where the web server runs as a different user from the file owner, check the file is still readable by PHP before you tighten it. A value of 440 or 400 also stops accidental edits, but see the note on key rotation below.
find /var/www/example.com -type d -exec chmod 755 {} \;
find /var/www/example.com -type f -exec chmod 644 {} \;
chmod 640 /var/www/example.com/wp-config.phpUseful security constants
These constants go above the line that says That's all, stop editing!. Add them one at a time and test.
DISALLOW_FILE_EDITremoves the theme and plugin code editors from the admin.DISALLOW_FILE_MODSgoes further: it also blocks installing and updating plugins, themes and core from the admin. Use it only if you deploy changes another way, and remember updates then become your responsibility.FORCE_SSL_ADMINforces https for sign-ins and the admin area.WP_DEBUGshould be false in production. If you need logs, setWP_DEBUG_LOGon andWP_DEBUG_DISPLAYoff, so errors go to a file instead of the page.
define( 'DISALLOW_FILE_EDIT', true );
define( 'FORCE_SSL_ADMIN', true );
define( 'WP_DEBUG', false );
// If you do need debug logs, keep errors off the page:
// define( 'WP_DEBUG', true );
// define( 'WP_DEBUG_LOG', true );
// define( 'WP_DEBUG_DISPLAY', false );Keys and salts
WordPress uses eight keys and salts to sign the cookies that keep people signed in. Replacing them signs everyone out at once, including an attacker with a stolen cookie. You can generate fresh values from the official WordPress secret-key service.
Change them after any suspected break-in, after a developer or employee leaves, and on a regular schedule. One note: if you make wp-config.php read-only, anything that edits the file for you, such as an automatic key rotation, cannot write to it. Choose between a stricter file mode and automatic rotation, or change the keys by hand.
Move or block the file
WordPress also looks for wp-config.php one directory above the site root, so you can keep it outside the public web folder. This only works if the parent directory is not itself a WordPress install.
If you cannot move it, tell the web server to refuse any direct request for it. Normally PHP runs the file and shows nothing, but a server misconfiguration or a backup copy like wp-config.php.bak can expose the source.
<Files wp-config.php>
Require all denied
</Files>
# Backup and editor copies of config files
<FilesMatch "^(wp-config.*\.(bak|old|orig|save|swp|txt)|.*~)$">
Require all denied
</FilesMatch>
# No directory listings
Options -IndexesThe same rules in nginx
nginx does not read .htaccess files, so the equivalent rules go in the server config. Reload nginx after editing.
location = /wp-config.php { deny all; }
location ~* /wp-config.*\.(bak|old|orig|save|swp|txt)$ { deny all; }
# No PHP execution in uploads
location ~* ^/wp-content/uploads/.*\.php$ { deny all; }
autoindex off;Keep PHP out of uploads
The uploads folder is meant for images, documents and media. If an attacker slips a PHP file into it, running that file gives them control. Block PHP execution there so a planted script becomes a harmless download.
<FilesMatch "\.(php|phtml|php[0-9])$">
Require all denied
</FilesMatch>Other files worth a look
A few more places deserve a quick check.
- Delete
readme.htmlandlicense.txtfrom the site root, or at least know they reveal your WordPress version. Updates put them back, so this is a small step. - Remove old backups, SQL dumps and zip files from the web folder. Search for
*.sql,*.zipand*.bak. - Use SFTP or SSH, not plain FTP, which sends passwords in the clear.
- Give each site its own hosting account or system user, so one infected site cannot read another's
wp-config.php.
Wp-config.php hardening: questions
What permissions should wp-config.php have?
640 or 600 is a good choice on most servers. Use 440 or 400 if you also want to prevent accidental edits, but test that PHP can still read the file.
Is it safe to move wp-config.php above the web root?
Yes, WordPress supports it, as long as the parent folder is not another WordPress install. It keeps the file out of the public folder entirely.
Should I use DISALLOW_FILE_MODS?
Only if you deploy changes some other way and have a plan for updates. It also blocks updates from the dashboard, so it can leave a site out of date if you forget.
Does changing the database table prefix help?
Only a little. It does not stop a real attack, and changing it on a live site is easy to get wrong, so spend your time on updates, access control and backups first.
How do I get new keys and salts?
Use the official WordPress secret-key service to generate a set, replace the eight define lines in wp-config.php and expect everyone to be signed out.