Blog · Guides

Hardening wp-config.php: Keys, Salts, and Kill Switches

Most WordPress hardening advice focuses on plugins and login pages, but a lot of real security work happens in one file: wp-config.php. It holds your database credentials, your authentication salts, and a set of constants that can turn off entire categories of risk with a single line. Getting it right is one of the highest-leverage things you can do, and it takes about twenty minutes.

Why wp-config.php deserves attention

wp-config.php sits above your webroot in most managed setups, or at the WordPress root otherwise, and it is read on every single request. It defines your database connection, your unique authentication keys and salts, and any of dozens of optional constants that control debugging, file editing, SSL enforcement, and more. If an attacker gets read access to this file, they get your database password. If they get write access, they can do almost anything. Treat it accordingly.

Rotate your keys and salts

The eight AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY constants (and their matching _SALT versions) are used to hash session cookies and various tokens. They should be long, random, and never reused across sites. If you suspect a leak, if you inherited a site from a previous developer or agency, or on a schedule of once or twice a year as routine hygiene, rotate them.

The wp-cli way is the cleanest:

wp config shuffle-salts

This regenerates all eight values in place and, as a side effect, invalidates every current logged-in session, including any session an attacker may have hijacked. That is a feature, not a bug. Warn users they will need to log back in.

If you cannot use wp-cli, WordPress.org’s official secret-key generator produces a ready-to-paste block. Replace the whole set at once rather than mixing old and new values.

Security constants worth setting

A handful of constants in wp-config.php do real work. Add them above the line that reads /* That's all, stop editing! */:

  • define('DISALLOW_FILE_EDIT', true); removes the theme and plugin editor from wp-admin. It does not stop file changes made another way, but it closes the easiest path for an attacker who has already gotten into an admin account.
  • define('DISALLOW_FILE_MODS', true); is stricter still: it also blocks plugin and theme installation and updates from the dashboard. Useful for sites where all deployment happens through a controlled pipeline, but it means you must update plugins another way, so do not set this and then forget about your update process.
  • define('FORCE_SSL_ADMIN', true); forces the entire wp-admin area and login process over HTTPS. If you are behind Cloudflare with Universal SSL terminating at the edge, this constant still matters for the connection between the edge and your origin, and for any direct-to-origin requests, so keep it on.
  • define('WP_DEBUG', false); on production, always. Debug mode can leak file paths and query details into error output that a visitor, or a bot, might see. Use debug mode on a staging copy instead.
  • define('AUTOMATIC_UPDATER_DISABLED', false); left at its default (or simply not set) lets WordPress apply minor core security releases automatically, which is generally what you want for a production site you are not watching every day.

Database credentials and keeping secrets out of the webroot

The most sensitive lines in the file are the four database constants: DB_NAME, DB_USER, DB_PASSWORD, DB_HOST. A few practices reduce the blast radius if the file is ever exposed:

  • Use a dedicated database user with privileges scoped to that one database, not a shared or root-level account. If credentials leak, the damage stays contained to that site’s data.
  • Where your hosting setup allows it, place wp-config.php one directory above the public webroot. WordPress checks for it there automatically, and it means a webserver misconfiguration that starts serving PHP as plain text cannot expose the file directly, because it is outside the served directory entirely.
  • Never commit wp-config.php to a public git repository. If you use version control for deployment, keep an example file (wp-config-sample.php style) in the repo and generate the real file with environment-specific values at deploy time, or gitignore it entirely.

Backups deserve the same caution. A full-site backup archive that includes wp-config.php is, by definition, a bundle containing your database password. Store backups somewhere access-controlled and off the same server, encrypt them if your backup tool supports it, and avoid emailing or Slacking a raw backup file around a team.

Kill switches: constants that help you in an emergency

A few lines can act as an emergency brake when something goes wrong:

  • define('WP_ALLOW_REPAIR', true); turns on WordPress’s built-in database repair tool at /wp-admin/maint/repair.php. Turn it on only while you need it, then remove the line, since anyone who finds that URL can run the repair tool.
  • A maintenance-mode plugin, or a manually created .maintenance file at the WordPress root containing a timestamp variable, will take the front end down cleanly while you work, which is useful if you are mid-hardening and want to avoid public traffic hitting a half-configured site.
  • If you suspect active compromise, rotating salts (above) immediately invalidates sessions, and changing the database password (updating both the database user and DB_PASSWORD in wp-config.php together) cuts off any script or backdoor that cached the old credentials.

A short hardening checklist

Beyond wp-config.php itself, pair this work with the standard account-level and edge-level hardening that reinforces it: strong, unique passwords with two-factor authentication for every admin account, limits on login attempts, XML-RPC disabled if you do not use the mobile app or pingbacks, least-privilege user roles so contributors are not accidentally editors, and backups that you have actually tested a restore from, not just scheduled. A web application firewall in front of the site (Cloudflare’s WAF, for instance) adds another layer that filters malicious requests before they reach wp-config.php’s logic at all, and keeping your origin IP unpublished behind that edge makes direct attacks on the server itself harder to mount.

You can audit your current constants quickly with wp-cli:

wp config list

This prints every defined constant and variable from wp-config.php, which is a fast way to confirm DISALLOW_FILE_EDIT, FORCE_SSL_ADMIN, and WP_DEBUG are set the way you expect before you consider the job done.

The takeaway

wp-config.php is small, boring, and easy to ignore, which is exactly why it is worth ten focused minutes. Rotate the salts, set the constants that match your workflow, keep the file and its backups out of reach, and you have closed off a meaningful set of attack paths with no plugin required.