Hardening isn't the same job as monitoring, and it's worth being clear about that before the checklist itself: hardening reduces the number of ways in; monitoring catches what still gets through despite that. Neither replaces the other. What follows is the hardening half — ordered by how much attack surface each change actually closes, not by how satisfying it feels to check off.
Lock down logins first — this is where most compromises start
Before touching a config file, get the basics of account access right, because credential-based attacks are the most common entry point by a wide margin:
- A strong, unique password and two-factor authentication on every administrator account. Not just the account you use daily — every account with admin-level access, including ones set up for a developer or agency that may not log in often.
- Never use "admin" or "administrator" as a username. WordPress's own hardening documentation calls this out directly: usernames like
adminorwebmasterare the first thing automated credential-guessing tools try, since half the guessing work (the username) is already done for them. - Rate-limit or lock out repeated failed logins at wp-login.php. Without this, nothing stops an automated script from trying thousands of password guesses against a real username at whatever rate your server allows.
Disable the in-dashboard file editor
WordPress ships with a built-in code editor under Appearance → Theme File Editor and Plugins → Plugin File Editor, letting any administrator edit PHP files directly from the browser. That convenience is also the fastest route an attacker takes once they've gained admin access by any other means — one edit, and they have arbitrary code execution with no upload step needed. Turn it off with one line in wp-config.php:
define('DISALLOW_FILE_EDIT', true);
This removes both editor menus for every user, administrators included — the pages return a permission error even if someone requests them directly by URL. The constant has been part of WordPress core since version 3.0, so it's safe to rely on across any reasonably current install. There's a stricter sibling worth knowing about too: DISALLOW_FILE_MODS does everything DISALLOW_FILE_EDIT does, plus blocks plugin/theme installs and core updates from inside the dashboard entirely — useful on a site where all deployments already go through a controlled process outside wp-admin.
Keep everything updated — and be honest about the real tradeoff
WordPress has supported automatic background updates since version 3.7 (2013), and turning on auto-updates for core, plugins, and themes closes the single biggest real-world attack vector: known, already-patched vulnerabilities in software nobody got around to updating. Most successful WordPress compromises exploit a flaw that was fixed weeks or months earlier.
The honest tradeoff is real, though: an auto-applied plugin or theme update can break a site — a function gets removed, a hook changes behavior, a theme's compatibility assumption stops holding. The reasonable middle ground most maintained sites land on: auto-update WordPress core's security releases and well-established plugins without hesitation, and stage major version bumps for anything business-critical or heavily customized on a test environment first. Falling behind "to be safe" is usually the riskier choice of the two.
Delete what you're not using — don't just deactivate it
A deactivated plugin or theme still has its files sitting on the server, and a vulnerability in a deactivated plugin's code can still be reachable directly by URL in some cases, depending on how the flaw works. If you're not using it and don't plan to, delete it rather than leaving it dormant. Fewer installed files means fewer things that need patching and fewer things that can be silently modified without you noticing.
Get file permissions right
This is one of the most commonly mis-set hardening items, partly because the "safe" default for a fresh file (644) is actually too permissive for one specific file. The standard guidance:
- Directories:
755(or the stricter750if your hosting setup supports it) - Files, generally:
644(or640stricter) wp-config.phpspecifically:440or400— meaningfully stricter than a normal file, because it holds your database credentials, authentication keys and salts, and any secret API tokens you've passed to plugins as constants. Leaving it at the default 644 is the most common mistake here, precisely because 644 looks like the safe, unremarkable choice for "just a file."
find /path/to/site -type d -exec chmod 755 {} \;
find /path/to/site -type f -exec chmod 644 {} \;
chmod 440 /path/to/site/wp-config.php
Stop leaking your usernames in the first place
The "don't use admin as a username" advice above only helps if your real usernames aren't already public. Two default WordPress behaviors hand them out for free: visiting yoursite.com/?author=1, ?author=2, and so on redirects to that user's author archive at a URL containing their exact username; and requesting yoursite.com/wp-json/wp/v2/users returns a JSON list of every registered user's display name and slug — which on most sites is the login name. Neither requires any authentication to view. An attacker doesn't need to guess your usernames if the site hands over a list of them before the password-guessing even starts. Restricting the REST API's users endpoint to logged-in requests only, and disabling the plain ?author= redirect, closes both without affecting anything a normal visitor or the block editor needs.
Rate-limit or disable XML-RPC if you're not using it
If nothing on your site depends on XML-RPC — check before you touch this, since some mobile publishing tools and plugin integrations do use it — turning it off removes a file that's disproportionately targeted for brute-force attacks specifically because it supports batching hundreds of login guesses into a single request. If you do need it, a rate limit on the file specifically neutralizes the same risk without losing functionality. See our breakdown of exactly how that attack works for the full mechanics.
Turn off directory browsing
By default, some server configurations will list every file in a folder if there's no index file present — handing an attacker a free directory listing of your uploads, themes, or plugin folders. A single line in .htaccess (on Apache) turns this off:
Options -Indexes
Stop broadcasting your exact WordPress version
By default, WordPress prints its version number in a meta tag in every page's <head>. That's a small thing on its own, but it's exactly the kind of detail automated scanners use to decide whether your site is worth targeting for a specific known vulnerability. Removing it is one function call in your theme's functions.php:
remove_action('wp_head', 'wp_generator');
Hardening reduces risk. It doesn't replace watching for compromise.
Every item above shrinks the number of ways in — that's genuinely valuable, and most of them take minutes to apply. None of them tell you if something already got through, or if a zero-day in a plugin you're running gets disclosed next week before you've had a chance to update. That's a different job: watching file integrity, login behavior, and admin account activity continuously, so a compromise gets caught early instead of found months later by an accident. Hardening and monitoring aren't competing priorities — they're two different halves of the same problem, and a site that only does one is still exposed on the other.
- WordPress.org: Hardening WordPress — the official handbook page this article draws from
- WordPress.org: Changing File Permissions
- OWASP WordPress Security Cheat Sheet
Harden the site, then let something watch for what gets through anyway.
Install free →