None of these steps is difficult. Most take a few minutes, and almost all of them are free. Together they remove the easy ways in, which matters because the overwhelming majority of WordPress compromises are not clever. They are an old plugin with a known flaw, a password reused from somewhere else, or a file left writable that should not have been.
Hardening does not make a site unbreakable, and anyone who says it does is selling something. What it does is move you out of the crowd of sites that automated scanners can take over without effort. Attackers run the same scripts against thousands of sites an hour; they move on when a site does not respond to the usual tricks.
The steps below are grouped by layer and ordered roughly by payoff. Work through them once, tick the checklist at the end, and put a reminder in your calendar to repeat the checks every quarter.
Accounts
Most login attacks are guesses against the username admin, using passwords leaked from other sites. Take away the first and the second becomes useless.
- Create a new administrator with an unusual username, log in as that user, and delete the default
adminaccount (assigning its posts to the new user). Check the author archive: if/?author=1redirects to a page showing the login name, that name is public, so make sure the display name differs from the login name. - Give every person their own account and a long unique password stored in a password manager. Twenty random characters you never type is better than a clever phrase you reuse.
- Turn on two-factor authentication for every administrator and editor. An authenticator app is a sound choice; text messages are better than nothing.
- Give people the lowest role that lets them work. A writer needs Author or Contributor, not Administrator. Review the list with
wp user list --fields=ID,user_login,rolesand remove people who have left.
Software
Outdated plugins are the main way in. The flaw is published, the exploit is shared, and scanners start probing within hours. The fix is dull: update promptly.
- Update core, plugins and themes as soon as a release is out, especially security releases. Minor core updates apply themselves by default; leave that on. For plugins, enable automatic updates for the ones you trust, and test the large ones (shops, page builders) on a staging copy if you can.
- Delete what you do not use. A deactivated plugin still sits on disk and its files can still be requested directly. The same goes for old themes: keep the active one and a default fallback, delete the rest.
- Be wary of any plugin that has not been updated in over a year, or whose author has stopped replying to support requests.
- Never use nulled or pirated premium plugins. They are frequently modified to include backdoors, and the "free" copy is a common route for infection.
You can list pending updates from the command line: wp plugin list --update=available. It takes seconds, so make it a habit.
Configuration
These changes happen in wp-config.php and a few server files. Take a backup of each file before editing.
Disable the built-in theme and plugin editor. If an attacker gets into the dashboard, the editor lets them write PHP straight into your site:
define('DISALLOW_FILE_EDIT', true);
There is a stronger setting, DISALLOW_FILE_MODS, which also blocks installing and updating from the dashboard. Use it only if you handle updates some other way, otherwise you will stop receiving them.
Protect wp-config.php, because it holds your database password. WordPress will load it from one directory above the web root, so on hosts that allow it, move it there. Otherwise deny web access to it and set its permissions to 640 or 600.
Refresh the authentication keys and salts if you suspect a breach. Changing them logs everyone out, which is the point: wp config shuffle-salts does it in one step.
Disable XML-RPC if nothing you use needs it. The file xmlrpc.php accepts many login attempts in a single request, which makes it a favourite for guessing passwords. With Apache 2.4:
<Files xmlrpc.php>
Require all denied
</Files>
Some mobile apps and a few plugins use XML-RPC, so test the things you rely on afterwards. Finally, limit login attempts, with a plugin or at server level with a tool such as fail2ban that watches the log for repeated failures and blocks the address for a while.
Server
Run a supported PHP version. PHP 8.x releases get security fixes for a limited period, then stop; anything older gets none. Check what you run with php -v or the hosting panel, and check that your plugins support the next version before upgrading.
Set file permissions to 755 for folders and 644 for files, with wp-config.php tighter. The files should be owned by your account's user and not be writable by "everyone". From the site root:
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod 600 wp-config.php
Block PHP execution in the uploads folder. Uploads should only ever hold images and documents; a PHP file there is almost always an attacker's backdoor. For Apache, put this in wp-content/uploads/.htaccess:
<FilesMatch "\.php$">
Require all denied
</FilesMatch>
On nginx the equivalent is a location ~* /wp-content/uploads/.*\.php$ { deny all; } block in the server configuration. Use HTTPS everywhere: install a certificate, redirect HTTP to HTTPS, and add define('FORCE_SSL_ADMIN', true); so login and admin traffic is never sent in plain text.
What changes by hosting type
On shared hosting, the provider handles the operating system, server software and account isolation, so your job is the application: accounts, plugins, configuration. You can usually set PHP version and permissions from the panel. On a VPS or dedicated server, the operating system, firewall, SSH and database are also yours; keep packages patched, allow SSH by key only, and open only the ports you use. On managed WordPress hosting, many of the server items above are done for you, but accounts and plugins remain your responsibility.
Watching
Hardening reduces the chance of an intrusion; watching tells you when one gets through. Keep a log of administrator logins and file changes. Run a periodic malware scan, and get alerts when a new administrator appears, since adding one is the first thing many intruders do.
Core files can be compared against the official release: wp core verify-checksums and wp plugin verify-checksums --all report any file that differs. To see what changed recently: find . -name '*.php' -mtime -3. Backups belong outside the hosting account, at another provider or in storage the site's login cannot reach, and a backup you have never restored is a hope rather than a backup.
If something goes wrong
Do not start deleting files at random. Take the site offline or behind a password, change every password (WordPress, database, hosting panel, FTP), and refresh the salts. Then decide: if you know exactly what happened, clean it; if you do not, restore from a clean backup taken before the problem began and apply updates before putting the site back. The troubleshooting guide and the stories in the casebook show what real clean-ups look like.
Commands worth running
Settings you have not tested are settings you hope are working. Ten minutes with a terminal tells you where you stand.
curl -I https://example.com/xmlrpc.phpshould return 403 once you have blocked it, instead of 405 or 200.curl -I https://example.com/wp-config.phpshould return 403 or 404, and never the file's contents or a blank 200.- Copy a harmless file called
test.phpinto the uploads folder, request it in a browser, and expect 403. Then delete it. curl -I http://example.com/should answer with a 301 to the HTTPS address.wp user list --role=administratorshould show only names you recognise.stat -c '%a %n' wp-config.phpshould print 600 or 640.
A worked example: a small business site had eleven plugins, three of them deactivated, and five administrators, two of whom had left the company. Deleting the dormant plugins, removing the two accounts and turning on two-factor took about twenty minutes. Nothing visible changed, which is how it should feel. Three months later one of the deleted plugins turned out to have a published flaw, and the site was not exposed to it.
Checklist
- Default admin account removed; unique usernames and passwords.
- Two-factor on all administrators and editors.
- Roles reviewed; leavers removed.
- Core, plugins and themes updated; unused ones deleted.
- File editing disabled; wp-config.php protected.
- XML-RPC blocked if unused; login attempts limited.
- PHP version current; permissions 755 and 644.
- PHP execution blocked in uploads.
- HTTPS forced, including admin.
- Login and file-change logging, scan and new-admin alerts in place.
- Off-site backups, restore tested.