WordPress Security Hardening: A Practical Checklist
Most WordPress hacks aren't sophisticated. They're bots hammering wp-login.php with leaked password lists, or an old plugin with a known vulnerability that nobody bothered to patch. Fix the boring stuff first, and you'll block the overwhelming majority of attacks before they even get started.
How WordPress sites actually get compromised
We see the same handful of entry points over and over on client sites:
- Brute-force login - bots trying thousands of username/password combos against
/wp-login.php. - Outdated plugins and themes - the single biggest cause. A plugin abandoned two years ago with a known CVE is an open door.
- Weak or reused admin passwords - especially on accounts created years ago that nobody rotated.
- Exposed
wp-config.phpor database credentials - through misconfigured permissions or a leftover backup file sitting in the web root. - Nulled/pirated themes and plugins - these frequently ship with a backdoor already baked in.
None of these need advanced defenses. They need discipline. Here's the checklist we run on every site we onboard.
1. Lock down authentication
Use strong, unique passwords
Every admin and editor account needs a unique password generated by a manager, not "CompanyName2024!". If you inherited a site, assume every old password is compromised and force a reset.
Enable two-factor authentication
Install a 2FA plugin (WP 2FA, Wordfence Login Security, or miniOrange) and require it for every account with publish or admin capabilities. This alone stops almost all credential-stuffing attacks, because the attacker also needs your phone.
Rename or restrict the login URL
Changing /wp-login.php to something custom (via a plugin like WPS Hide Login) won't stop a determined attacker, but it silences 90% of automated login-scanning bots overnight. Combine it with an IP allowlist in .htaccess if you have a static office IP:
<Files wp-login.php>
order deny,allow
deny from all
allow from 203.0.113.10
</Files>Limit login attempts
Use a plugin (Limit Login Attempts Reloaded is lightweight and free) to lock out an IP after 5 failed tries. If you're on a VPS, fail2ban with a WordPress jail does the same thing at the server level and is harder to bypass.
2. Keep everything updated - without breaking the site
Outdated software is the number-one cause of compromise, full stop. But blind auto-updates on a production site can break things too. The middle ground:
- Enable automatic updates for minor WordPress core releases (on by default) and for security-only plugin updates where the plugin supports it.
- For major plugin/theme updates, test on a staging copy first - most managed hosts, including Getwebup, offer one-click staging.
- Remove anything you're not using. An inactive plugin is still a vulnerability; deactivating isn't enough, delete it.
- Never install a nulled/pirated premium plugin or theme. If budget is the issue, there's almost always a well-maintained free alternative.
3. Harden wp-config.php and file permissions
Move wp-config.php one directory above the web root if your hosting setup allows it (WordPress will still find it). If not, at minimum lock down permissions:
chmod 640 wp-config.php
chmod 755 wp-content wp-includes wp-admin
find wp-content -type d -exec chmod 755 {} \;
find wp-content -type f -exec chmod 644 {} \;Also add unique authentication keys and salts - generate a fresh set from the WordPress secret key API and paste them into wp-config.php. This invalidates any stolen session cookies immediately.
Disable the theme and plugin file editor
By default, any admin can edit PHP files straight from Dashboard > Appearance > Editor. If an attacker gets admin access, that's a one-click backdoor. Turn it off:
define('DISALLOW_FILE_EDIT', true);Add that line to wp-config.php and the editor menu disappears entirely, for every user including yourself.
4. Reduce your attack surface
- Hide the WordPress version from your page source - it tells attackers exactly which known exploits to try.
- Disable XML-RPC if you don't use the mobile app or Jetpack, since it's a common target for brute-force amplification. Most security plugins can toggle this with one click.
- Block PHP execution in
wp-content/uploadsso an uploaded file can never run as a script even if a vulnerable plugin lets something slip through. - Restrict directory listing so someone can't browse
yourdomain.com/wp-content/plugins/and see every plugin (and version) you're running.
In cPanel, uploads-folder PHP blocking is a two-line .htaccess drop into that folder:
<FilesMatch "\.php$">
Deny from all
</FilesMatch>5. Back up like you expect to get hacked
Hardening reduces risk, it doesn't eliminate it. A working, tested, off-server backup is what actually saves you on a bad day. Follow the 3-2-1 rule: 3 copies, on 2 different types of storage, with 1 stored off-site. If you're hosting with Getwebup, automated daily backups are included - just confirm restore points are actually being generated, not only scheduled.
6. Add a security plugin - but don't stack three
One good security plugin (Wordfence, Sucuri, or iThemes Security) covers firewall rules, malware scanning, and login protection in a single package. Running two or three at once causes conflicts and slows the site down without adding real protection. Pick one, configure it properly, and let it do its job.
Quick reference table
| Risk | Fix | Effort |
|---|---|---|
| Brute-force login | 2FA + login limiter + custom login URL | 15 min |
| Outdated plugins | Remove unused, update on staging first | Ongoing |
| Stolen cookies | Fresh auth keys/salts in wp-config.php | 5 min |
| Backdoor via editor | DISALLOW_FILE_EDIT | 2 min |
| Uploaded PHP shells | Block PHP execution in uploads/ | 5 min |
| Total loss on compromise | Tested off-site backups | Ongoing |
Prevention checklist
- Run through this list once when you launch a new site, then again every 3-6 months.
- Audit user accounts quarterly - remove anyone who no longer needs access.
- Watch for unexpected admin accounts or scheduled tasks - a classic sign of an existing compromise.
- Keep PHP itself updated at the server level, not just WordPress core.
None of this is exotic. It's the security equivalent of locking your front door and not leaving the spare key under the mat - unglamorous, but it's what actually stops the break-ins that happen every day.
Frequently asked questions
Is a security plugin enough on its own, or do I need server-level protection too?
A security plugin handles application-level threats like login brute-forcing and known malware signatures, but it can't stop everything - a compromised neighboring site on shared hosting, for example, is outside its reach. Server-level protections like a WAF, isolated hosting accounts, and regular malware scanning at the hosting layer cover that gap. Use both.
Will hardening steps like DISALLOW_FILE_EDIT break any legitimate plugins?
Very rarely. A handful of page builders or theme customizers expect to write files through the dashboard editor, but almost all modern plugins use their own settings UI instead. If something breaks after adding the constant, comment it out temporarily, confirm the plugin needs it, and consider editing files via SFTP instead of re-enabling the built-in editor.
How do I know if my WordPress site has already been hacked?
Common signs: unexpected admin users, unfamiliar scheduled cron jobs, strange redirects for visitors coming from Google, a sudden spike in outbound email (spam), or your host flagging malware in a scan. If you see any of these, change all passwords immediately, restore from a known-clean backup, and then apply the hardening steps above before reconnecting the site to the internet.
Do I still need backups if my site is fully hardened?
Yes, always. Hardening reduces the odds of a compromise, it doesn't make one impossible - zero-day vulnerabilities and supply-chain attacks (a compromised plugin update, for instance) can bypass even a well-configured site. Backups are your actual recovery plan when prevention fails.
Should I disable XML-RPC even if I use the Jetpack or WordPress mobile app?
No - those tools rely on XML-RPC to function, so disabling it entirely will break them. Instead, restrict XML-RPC to only the methods you actually need (most security plugins let you allow pingbacks off while keeping the app API on), or firewall it to known IPs if your usage is predictable.