WordPress sites are rarely broken into by someone targeting you personally. The overwhelming majority of compromises are automated: a script crawls the web looking for a specific outdated plugin version, finds thousands of matches, and exploits all of them the same afternoon. That single fact should shape how you defend a site. You are not trying to outsmart an attacker. You are trying to not be in the list the script is working through.
That makes the priorities clear, and they are not the ones most security articles lead with. Keeping software current stops more attacks than any plugin you can install. Everything below is ordered by how much real risk it removes.
Update everything, and mean it
Outdated plugins are the single largest cause of compromised WordPress sites. When a vulnerability is disclosed, the fix and the exploit become public at the same moment, and scanning for unpatched installs begins within hours. The window between disclosure and attack is measured in days, sometimes less.
Turn on automatic updates for plugins and themes, and for minor core releases. Minor core releases are security and bug fixes; they are deliberately conservative and very rarely break a site. Major releases deserve more care, and the article on enabling automatic updates goes into how to split those apart.
The part people skip: delete what you are not using. A deactivated plugin still has its code on disk, and some vulnerabilities are reachable without the plugin being active. An unused theme is the same story. If you are not using it, remove it, do not merely switch it off.
Make the login worth nothing to an attacker
Automated login attempts against wp-login.php are constant on every WordPress site on the internet. They are not a sign that you have been targeted; they are background noise. What matters is whether any of them can succeed.
Use a long, unique password for every administrator account, unique meaning it exists nowhere else, so a breach at some unrelated service cannot be replayed against your site. A password manager makes this practical; without one, people reuse passwords no matter how many times they are told not to.
Add two-factor authentication for accounts that can install plugins or edit files. This is the single change that makes a stolen password useless, and it costs a few seconds per login.
Limit login attempts so repeated failures from one address are slowed or blocked. This does not make guessing impossible, it makes it too slow to be worth the attacker's time, which is the whole game against automated attacks.
Finally, give every person their own account with the lowest role that lets them do their job. An editor who only writes posts does not need administrator rights, and if that account is compromised the damage is bounded.
Protect wp-config.php and get file permissions right
wp-config.php holds your database credentials in plain text. If it is readable by someone who should not have it, nothing else you do matters.
Standard permissions on shared hosting are 644 for files and 755 for directories. wp-config.php can be tightened to 640 or 600. What you must never do is set anything to 777. It appears in a great deal of old forum advice as a fix for upload problems, and it makes the file writable by anyone. It converts a minor inconvenience into a full compromise.
If uploads or updates fail with a permissions error, the correct fix is ownership, not 777. Open a ticket rather than working around it; the underlying cause is usually a file owned by the wrong user after a manual upload.
Replace the security keys in wp-config.php if you suspect a compromise. Regenerating them invalidates every existing session, which immediately logs out anyone who has stolen a cookie:
define('AUTH_KEY', 'paste a fresh value here');
define('SECURE_AUTH_KEY', 'paste a fresh value here');
define('LOGGED_IN_KEY', 'paste a fresh value here');
define('NONCE_KEY', 'paste a fresh value here');
WordPress publishes a generator that produces a full set of random values. Paste the whole block over the existing one.
Turn off the file editor in the admin area
WordPress lets administrators edit theme and plugin PHP directly from the dashboard. It is convenient, and it is also the fastest route from a stolen administrator password to arbitrary code running on your server. Almost nobody needs it, because real edits belong in a child theme edited over SFTP.
Disable it by adding one line to wp-config.php:
define( 'DISALLOW_FILE_EDIT', true );
This costs you nothing if you edit files properly, and removes an entire category of post-compromise escalation.
Use HTTPS everywhere, not just on the login page
Without HTTPS, session cookies travel in the clear and anyone on the same network can read them. Partial HTTPS, secure checkout, plain HTTP elsewhere, leaks the session cookie on the plain pages, which defeats the point.
Ahosting includes a free SSL certificate, so there is no cost argument for leaving it off. Once the certificate is active, set both the WordPress Address and Site Address to the https:// version and redirect HTTP to HTTPS. Setting up an SSL certificate in WordPress explains the steps and the mixed-content cleanup that usually follows.
Be selective about security plugins
A security plugin can add useful things: login attempt limits, two-factor, file change detection, activity logging. It cannot make an outdated plugin safe, and it cannot undo a 777 directory.
Install one, not three. Multiple security plugins fight over the same hooks, produce contradictory logs and occasionally lock you out of your own site. Pick one that is actively maintained and turn on the features you will actually read the output of. A firewall log nobody looks at provides no security.
Be sceptical of anything promising to "hide" WordPress. Moving the login URL and removing version numbers stops naive scanners and nothing else. It is not harmful, but do not mistake it for a defence.
Back up as though the other layers will fail
Every layer above tries to prevent a compromise. Backups are what you have left when one of them did not work, and they are the only item on this list that helps after the fact.
A backup stored only on the same server is not a backup. If the account is compromised or the disk fails, both copies go together. Keep at least one copy somewhere else, and keep enough history that you can go back past the point of infection: compromises are frequently discovered weeks after they happen, and a backup rotation of three days may only contain infected copies.
Then test a restore. An untested backup is a hypothesis. Managing WordPress backups explains the schedule and the restore drill.
If you think a site is already compromised
Do not start by deleting suspicious files. Change all administrator passwords and regenerate the security keys first, so whoever is inside loses their session while you work. Then take a copy of the current state before you clean anything. You may need it to understand how they got in.
Restore from a backup taken before the compromise rather than trying to remove injected code by hand. Hand cleaning misses backdoors routinely; a single leftover file returns the attacker to the site a week later. After restoring, update everything before putting the site back online, otherwise the same automated scan finds the same unpatched plugin again.
A realistic checklist
- Automatic updates on for plugins, themes and minor core releases.
- Unused plugins and themes deleted, not just deactivated.
- Unique password plus two-factor on every administrator account.
- Login attempts limited.
- Users on the lowest role that lets them work.
- Files 644, directories 755,
wp-config.phptighter, nothing at 777. DISALLOW_FILE_EDITset.- HTTPS on the whole site, HTTP redirected.
- One maintained security plugin, configured and actually monitored.
- Off-server backups with enough history, and a restore you have tested.
The WordPress hardening documentation goes further for sites with unusual requirements, and is worth reading once the list above is genuinely complete.
Two interfaces deserve separate treatment, and the usual advice about them is half wrong. Securing the WordPress REST API and XML-RPC explains which to block and which to leave alone.
Restrictions that must not be switched off belong somewhere the dashboard cannot reach. How to Use MU-Plugins and a Site-Specific Plugin sets out that placement.