WordPress and CMS · 8 min read

WordPress security hardening checklist for non-developers

WordPress runs over 40% of websites and is the most-targeted CMS on the internet. The good news: a clear ten-step hardening checklist closes most attacks, and none of the steps require a developer.

A
Andre Reis

WordPress runs about 43% of websites globally. That popularity makes it the single most-targeted CMS on the internet, but it also means there is a clear, well-understood checklist of fixes that closes most attacks. None of them require a developer. Most take less than ten minutes. This article walks the checklist in priority order, explains what each fix stops, and points out where to do each one inside the standard WordPress admin.

The five fixes that stop most attacks

1. Keep WordPress core, themes, and plugins updated

The single biggest cause of compromised WordPress sites isn't sophisticated zero-day attacks. It's bots scanning for sites running known-vulnerable plugin versions and exploiting the publicly-known bug. Wordfence's annual reports consistently show that over 90% of compromise attempts target a vulnerability that already has a patch.

What to do:

  • In WordPress admin, Dashboard, Updates. Click Update on anything outdated. Today.
  • Confirm auto-updates for WordPress minor releases are on (they are by default).
  • Enable plugin auto-updates for non-critical plugins. In Plugins, Installed Plugins, the right-most column has an "Enable auto-updates" link per plugin.
  • Don't auto-update payment gateways or anything you've customised. Test those manually after each release.
  • Delete plugins and themes you don't actually use. Inactive plugins still get scanned and exploited because their PHP files sit on disk and are reachable directly.

A useful rule of thumb: if a plugin hasn't been updated in 18 months, it isn't being maintained. Replace it.

2. Strong admin passwords plus two-factor authentication

The second most common WordPress attack is simply guessing the admin password. Bots try 'admin' / 'password', 'admin' / 'admin123', or rotate through breach databases.

Fix in two parts:

  • Long, unique passwords for every WordPress account. WordPress's "Generate Password" button on the user profile page produces 24-character random strings. Use that and store the result in a password manager.
  • Two-factor authentication on every admin and editor account. The free WP 2FA or Two Factor Authentication plugins both work well. Enable for all administrator and editor roles, not just yours.

The username admin is also a soft target. If you have a user literally called admin, create a new account with a different username, give it admin rights, log in as the new user, then delete the admin user (assigning their content to the new account during deletion).

3. Limit login attempts

Without rate limiting, an attacker can attempt thousands of password guesses per minute against /wp-login.php. With rate limiting, three or four wrong attempts locks them out for an hour.

The free Limit Login Attempts Reloaded plugin handles this in five clicks. Sensible settings:

  • Allowed retries: 4
  • Lockout duration: 20 minutes
  • Hours until retries are reset: 24

You'll never notice it as a legitimate user. Bots will give up and move on.

4. Disable theme and plugin file editing from the admin

WordPress includes a built-in code editor under Appearance, Theme File Editor and Plugins, Plugin File Editor. If an attacker compromises an admin account, that editor lets them paste malicious PHP directly into a theme file with no upload required. The editor exists for developer convenience that almost nobody actually uses through the admin.

Open wp-config.php (your hosting provider's file manager, or sFTP) and add this line near the top, just before the comment "That's all, stop editing!":

define('DISALLOW_FILE_EDIT', true);

Save. The editors disappear from the admin menu. If you actually need to edit a theme file, do it via sFTP or your host's file manager.

5. Install one (and only one) reputable security plugin

Pick one of:

  • Wordfence Security: free tier is generous. Web Application Firewall, malware scanner, login security, all in one.
  • Solid Security (formerly iThemes Security): clean UI, sensible defaults, free tier covers the essentials.
  • Sucuri Security: free version is monitoring-only; paid version includes a hosted firewall in front of your site.

Don't install multiple security plugins. They fight each other and slow the site to a crawl. Pick one, enable the recommended-defaults profile, and let it run.

After install, run the malware scanner once. Most security plugins scan automatically thereafter, but a manual scan after each plugin update or theme change is good hygiene.

Server-level hardening

The five fixes above stop most attacks. The server-level checklist below stops the rest.

Disable XML-RPC if you don't use it

xmlrpc.php is a legacy WordPress endpoint used by older mobile apps and pingbacks. It is also the second most-attacked WordPress endpoint after wp-login.php, because a single XML-RPC request can attempt many login passwords at once.

If you don't use the WordPress mobile app, the Jetpack plugin, or pingbacks, disable XML-RPC. Most security plugins have a one-click toggle. Or paste this in your theme's functions.php:

add_filter('xmlrpc_enabled', '__return_false');

Or block at the web server level by adding to your .htaccess (Apache):

<Files xmlrpc.php>
    order deny,allow
    deny from all
</Files>

Disable user enumeration

By default, requesting yoursite.com/?author=1 returns a redirect that reveals the username of user ID 1. Attackers use this to discover usernames before brute-forcing passwords.

Most security plugins have a "Block User Enumeration" toggle. Use it. Or add to functions.php:

if (!is_admin() && isset($_GET['author'])) {
    wp_safe_redirect(home_url(), 301);
    exit;
}

Hide your WordPress version

The WordPress version is published in your site's HTML head and in readme.html at the root of your install. Both are useful only to attackers checking which CVEs apply.

Add to functions.php:

remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');

And delete readme.html and license.txt from your WordPress root via sFTP. They serve no purpose for visitors.

Disable directory listing

If you visit yoursite.com/wp-content/uploads/2024/03/ and see a list of every file in that folder, directory listing is on. Attackers love this. Disable by adding to your .htaccess:

Options -Indexes

For nginx:

autoindex off;

Most modern hosts have this off by default; check yours by visiting an /uploads/ subfolder URL directly.

Restrict wp-admin to your IP if you can

If your team always works from the same office or your home VPN, lock /wp-admin/ to those IPs only. Other people get a 403.

In .htaccess inside wp-admin/:

order deny,allow
deny from all
allow from 203.0.113.45
allow from 203.0.113.46

This is overkill for marketing-driven sites where guest authors log in from coffee shops. But for sites with a small fixed set of editors, it removes the brute-force attack surface entirely.

File and database security

Set sensible file permissions

Inside your hosting control panel or via sFTP:

  • All files: 644 (-rw-r--r--)
  • All directories: 755 (drwxr-xr-x)
  • wp-config.php: 600 (-rw-------)

Anything more restrictive (444, 400) is fine. Anything more permissive (especially 777) is a critical issue and will get the site compromised quickly.

Move wp-config.php above the web root

WordPress automatically looks for wp-config.php one level above the WordPress install directory. If your install is in /public_html/, you can move wp-config.php to /. That puts the database password and salts above the web root entirely, where no web request can ever read them.

Not all shared hosts allow this. If yours does, take five minutes and move the file.

Use a non-default database table prefix

WordPress installs default to wp_ as the table prefix. SQL injection attacks often hard-code that prefix. Changing it during install (or after, with a migration plugin like Brozzme DB Prefix & Tools Addon) reduces the success rate of automated attacks.

Pick a short random prefix like wp_a7f9_. The trailing underscore must stay.

Disable PHP execution in /uploads/

Attackers who manage to upload a malicious file (typically through a vulnerable plugin) need to execute it. The /uploads/ folder is the most common attack vector because it's the only folder that accepts uploads by default.

Add a .htaccess inside wp-content/uploads/ containing:

<Files *.php>
    deny from all
</Files>

That single block prevents any PHP file in /uploads/ from being executed via web request, even if an attacker manages to write one there.

For nginx, the equivalent in your server config:

location ~* /wp-content/uploads/.*\.php$ {
    deny all;
}

Backups: the only thing that saves you when nothing else does

If everything else fails and your site is compromised, the recovery path is to roll back to a clean backup. Without one, you're rebuilding from scratch.

The minimum viable backup setup:

  • UpdraftPlus: free WordPress plugin, schedules automated backups to Google Drive, Dropbox, or S3.
  • Frequency: daily for a busy site, weekly for a low-change site. Whatever you pick, schedule it; manual backups never happen.
  • Retention: keep at least the last seven daily backups and four weekly ones. Compromise can sit undetected for weeks before you notice.
  • Test restores quarterly: a backup you've never restored from isn't proven to work. Once a quarter, restore the latest backup to a staging URL and confirm the site comes up.

If your host offers automated backups (most do), still run an independent backup. Host backups disappear when the host has problems.

What to do if you suspect compromise

If your site is loading strange ads, redirecting to gambling sites, sending spam from your domain, or has files you don't recognise:

  1. Don't panic and delete things. Whatever the attacker put there is evidence; you'll need it to understand the scope.
  2. Take the site offline temporarily by enabling a maintenance-mode plugin or putting a static index.html in place at the root.
  3. Change every password: hosting account, WordPress admins, FTP, MySQL, plus any third-party services your site connects to (Stripe, Mailchimp, etc.). Assume they are all compromised.
  4. Run a full malware scan with Wordfence's free scanner or the equivalent in your security plugin.
  5. Restore from a backup taken before the compromise. Check file modification dates to estimate when the compromise started.
  6. If customer personal information was accessed, you may have an "eligible data breach" obligation under the Notifiable Data Breaches scheme. Notify the OAIC within 30 days of becoming aware.

If the compromise is severe or you're unsure, engage a WordPress security specialist. Sucuri, Wordfence, and many Australian security consultancies offer paid clean-up services starting around AUD $300.

Run the free scan

Run our free scanner on your WordPress site to see which of these hardening items are visible to attackers right now: the WordPress version, exposed admin paths, missing security headers, weak login surfaces, and outdated plugin signatures. The technical report walks you through each finding with the exact fix.

Free scan

Want to know if any of this applies to your domain?

Run a free 30-second scan to see what your website and email setup actually look like.

Run free scan →