After cleaning up a lot of compromised WordPress sites, I’ve noticed something that surprises most owners. The attacks are rarely sophisticated. Nobody spent weeks studying their business. An automated bot found an outdated plugin, a weak admin password or a writable folder, and it did what bots do.
That’s actually good news. If most attacks are automated and lazy, then a handful of sensible hardening steps removes you from the list of easy targets. You don’t need to be unhackable. You need to be harder to break into than the millions of sites that haven’t bothered.
This guide is the checklist I apply to sites I manage. Some steps take thirty seconds, some need server access. I’ve explained why each one matters, because a security setting you don’t understand is a setting you’ll eventually undo by accident.
Start with the boring basics
Before touching configuration files, get the fundamentals right. These three things prevent the majority of infections I see.
Keep everything updated. Core, plugins and themes. When a vulnerability in a popular plugin is published, attackers start scanning for it within hours, sometimes minutes. A plugin that’s six months out of date is an open invitation. If updates scare you because things break, the answer is a staging site, not skipping updates.
Remove what you don’t use. Deactivated plugins are still sitting on the server, and many vulnerabilities can be exploited without the plugin being active. The same goes for old themes. Keep your active theme and one default theme as a fallback, and delete the rest.
Use strong, unique passwords for every admin. I still find administrator accounts with passwords like the company name followed by 123. Use a password manager, and turn on two-factor authentication for every account that can publish or change settings.
Lock down wp-config.php
The wp-config.php file holds your database credentials and security keys. It also accepts a few constants that close common attack paths.
DISALLOW_FILE_EDIT is the one I care about most. The built-in theme and plugin editor is the first place an attacker goes after stealing an admin password, because it lets them paste a backdoor straight into a PHP file. Turning it off costs you nothing. If you really need to edit code, you should be doing it over SFTP or Git anyway.
There’s also DISALLOW_FILE_MODS, which goes further and blocks installing or updating plugins from the dashboard. It’s useful on sites where updates are deployed through a proper workflow, but don’t enable it unless someone is actually handling updates another way. A locked site that never gets patched is worse than an unlocked one that does.
Also check that your security keys and salts are set and unique. If they still look like placeholder text, generate new ones from the official WordPress.org secret key service and paste them in. Changing them logs out every user, which is exactly what you want after any suspected compromise.
Get file permissions right
Wrong file permissions are behind a surprising number of reinfections. If the web server can write to every file, then any vulnerability that allows file writes gives an attacker free rein.
A sensible baseline on most Linux hosts looks like this:
- Directories: 755
- Files: 644
- wp-config.php: 440 or 400, or 600 if your setup needs the owner to write to it
You can apply the baseline from the site root:
Ownership matters as much as the numbers. Ideally your files are owned by a deployment user, and the web server user only has write access where it truly needs it, mainly wp-content/uploads. On shared hosting you may not have that level of control, and that’s fine. On your own VPS, it’s worth setting up properly.
Never set anything to 777. If a plugin tells you it needs 777 to work, find a different plugin.
Block PHP execution in the uploads folder
The uploads folder is meant for images, PDFs and similar files. There is no legitimate reason for a PHP file to run from there. Yet it’s one of the most common places I find backdoors, because it’s writable by design.
On Nginx, add this inside your server block:
On Apache, create an .htaccess file inside wp-content/uploads:
| <code><FilesMatch "\.(?i:php | phtml | php5 | php7 | phar)$"> |
|---|
Even if an attacker manages to upload a malicious file through a vulnerable form, this stops it from running. It’s one of the highest value, lowest effort changes on this list.
Decide what to do about XML-RPC
XML-RPC is an older remote publishing interface. Most modern sites don’t need it, since the REST API replaced most of its uses. The problem is that it’s a favourite target for brute-force attacks, because a single request can try hundreds of passwords at once.
If you don’t use the Jetpack mobile features or an old desktop publishing tool that depends on it, block it at the server level. On Nginx:
Blocking it at the server is better than using a plugin, because the request never reaches PHP. That saves resources during an attack as well as stopping it.
Reduce user enumeration
Bots like to discover usernames before they start guessing passwords. WordPress makes this a little too easy in two places: author archive URLs like /?author=1, and the public REST endpoint that lists users.
You can’t make usernames truly secret, and you shouldn’t rely on hiding them. But there’s no need to hand them out either. Make sure your display names are different from your login names, and consider restricting the users endpoint to logged-in users if your site doesn’t need it public. Combined with two-factor authentication and rate limiting, a known username becomes much less useful to an attacker.
Add security headers
Security headers tell the browser how to behave with your site. They won’t stop a server-side hack, but they protect visitors from a range of browser-based attacks. A good starting set:
Be careful with Strict-Transport-Security. Once a browser sees it, it will refuse plain HTTP for your domain for the whole max-age period. Only add it once HTTPS works perfectly everywhere, including subdomains if you use includeSubDomains.
A Content Security Policy is the most powerful header and also the easiest to get wrong. WordPress sites tend to load scripts from many places, and a strict policy can break things quietly. Start in report-only mode, watch what it flags, and tighten it over time.
Turn off directory listing
If a folder has no index file, some servers will happily show a list of everything in it. That gives attackers a free map of your plugins and backups. On Apache, add this to your main .htaccess:
Nginx doesn’t list directories unless autoindex is turned on, but it’s worth checking your config to be sure.
Protect the login page
The login page gets hammered by bots every day. A few layers make a big difference:
- Two-factor authentication for every administrator and editor.
- Rate limiting, either with a plugin, with fail2ban on your server, or at a firewall such as Cloudflare.
- Limiting admin access by IP address, if your team works from fixed locations.
Changing the login URL is popular, and it does reduce noise in your logs. Just don’t mistake it for real protection. It’s a curtain, not a lock.
Put a firewall in front of the site
A web application firewall filters malicious requests before they reach WordPress. You can run one as a plugin, but I prefer a DNS-level service or a server-level firewall, because it stops bad traffic before PHP and the database have to do any work. That matters during an attack, when a plugin-based firewall can slow the site down simply by processing all the junk.
Watch for changes
Hardening reduces risk. Monitoring tells you when something slips through anyway. At a minimum, set up:
- File integrity monitoring, so you know when core or plugin files change unexpectedly.
- An activity log, so you can see who logged in, who changed what, and when new admins were created.
- Uptime monitoring, because some infections are noticed first as a site that keeps going down.
You can also check core files yourself any time with WP-CLI:
Backups are part of security
No hardening checklist is complete without backups that are stored away from the server. If everything else fails, a clean, recent backup is how you get back online in an hour instead of a week. Keep several weeks of history, because infections often sit quietly for a while before anyone notices.
What not to bother with
Some popular advice gives very little real protection. Changing the database table prefix from wp_ helps slightly against badly written automated SQL injection, but it won’t save a site with a vulnerable plugin. Hiding the WordPress version number is similar. Neither hurts, but don’t spend your energy there while basics like updates and two-factor authentication are missing.
Frequently asked questions
Do I need a security plugin?
Not necessarily, but most site owners benefit from one, mainly for login protection, activity logging and file change alerts. What matters is that the features are covered somewhere. If your host or server already provides a firewall and malware scanning, a lightweight plugin for two-factor authentication and logging may be all you need.
Will these changes break my site?
The wp-config constants, file permissions and PHP blocking in uploads almost never break anything. Security headers and XML-RPC blocking occasionally affect specific integrations. Make changes one at a time on staging and test your key features after each one.
How often should I review security?
Updates should happen weekly. A short review of admin users, active plugins and logs once a month is enough for most sites. Do a full hardening check after any major change, such as a host migration or redesign.
My site was already hacked. Is hardening enough?
No. Hardening a compromised site just locks the attacker in with you. Clean the site fully first, find the entry point, rotate every password and key, and then apply these steps.
The bottom line
Security isn’t one plugin or one setting. It’s a set of habits: update often, remove what you don’t need, limit what the web server can write, protect logins and watch for changes. Each step on its own is small. Together they make your site a much harder target than most, and for automated attackers, that’s usually enough to make them move on.




