Blog · Security

The One-Hour DIY WordPress Security Audit

Most WordPress compromises are not clever. They are opportunistic. An attacker’s tooling scans for stale plugins, weak passwords, loose file permissions, or exposed endpoints, and it moves on if none of those are present. You do not need to be a security researcher to close most of that gap. You need an hour and a checklist. Here it is, organized into five passes: users, updates, permissions, exposure, and backups.

Pass One: Users and Access

Start here because access control is the thing attackers actually want. Use wp-cli to get a clean list of every account and its role:

wp user list --fields=ID,user_login,user_email,roles

Look for three things. First, any account with the Administrator role that should not have it, including old contractor logins or a generic “admin” username left over from setup. Second, accounts tied to email addresses you no longer recognize or control. Third, roles that are broader than the person’s actual job. An author account does not need editor privileges, and an editor does not need admin.

Delete or downgrade anything that fails this test:

wp user delete 12 --reassign=1
wp user set-role 8 editor

While you are in here, make sure every remaining admin account has a strong, unique password, and turn on two-factor authentication if you are not already using it. A compromised password is still the single most common way in.

Pass Two: What’s Actually Out of Date

Outdated core, plugins, and themes are the second most common way in, because known vulnerabilities in old versions are public knowledge. Check your current state in one shot:

wp core version
wp plugin list --format=table
wp theme list --format=table
wp core check-update

Update everything that has an update available, but do not stop there. Look for plugins and themes that are merely installed and inactive. A deactivated plugin with a known vulnerability is still exploitable if its files are sitting on disk, since many exploits target the plugin’s files directly rather than going through WordPress’s activation checks. Remove anything you are not actively using:

wp plugin delete old-plugin-slug
wp theme delete unused-theme-slug

Also confirm your PHP version. PHP 8.3 with OPcache is a solid, current default, and it gets you both performance and the ongoing security patches that older PHP branches no longer receive.

Pass Three: File and Directory Permissions

Permissions that are too loose let a single compromised file infect everything else. The standard, safe baseline is directories at 755 and files at 644, with wp-config.php tightened further since it holds your database credentials:

find /path/to/site -type d -exec chmod 755 {} \;
find /path/to/site -type f -exec chmod 644 {} \;
chmod 600 wp-config.php

Run those from your site’s root, adjusting the path for your setup, and confirm ownership is set to your web server user rather than root or a shared account. While you are here, disable the built-in plugin and theme file editor in wp-admin. It is a convenience feature that also happens to be a direct code-execution path if an attacker gets into an account:

define('DISALLOW_FILE_EDIT', true);

Add that line to wp-config.php if it is not already there.

Pass Four: Reduce Your Exposure Surface

Every endpoint you expose is something an attacker can probe. A few worth checking specifically:

  • Login page brute forcing. wp-login.php is a known target. Rate limiting or a WAF rule that challenges repeated failed logins cuts this off cleanly.
  • User enumeration. The default REST API and author archive pages can leak valid usernames, which feeds directly into brute force attempts. If you do not need the REST API’s user endpoints publicly, restrict them.
  • XML-RPC. Unless you specifically need it for a mobile app or a remote publishing tool, disabling it removes a path used for both brute forcing and amplification attacks.
  • Directory listing. Confirm your server is not showing a raw file listing for wp-content/uploads or plugin folders when there is no index file present.

If you are running Cloudflare in front of your site, this is also the moment to confirm the origin IP is not publicly discoverable. Cloudflare’s edge gives you a WAF, bot mitigation, and DDoS protection, but only if traffic is actually forced through it. An exposed origin IP lets an attacker skip the edge entirely and hit your server directly.

Pass Five: Verify Backups Actually Work

A backup you have never restored is a hope, not a plan. Confirm three things: backups are running on a schedule, they are stored somewhere other than the same server (a second disk on the same box does not survive a server-level incident), and you have actually tested a restore at some point, even to a staging copy. If any one of those three is missing, fix it before you move on to anything else on this list. Everything above reduces the odds of a compromise. Backups are what get you back online if one happens anyway.

This is one area where a managed host can genuinely take the task off your plate; automated, verified, off-server backups are something ServerBorn handles as part of the hosting stack, which is worth knowing if you would rather not build and test this process yourself.

The Takeaway

None of these five checks require deep security expertise, and together they take about an hour. Users, updates, permissions, exposure, and backups cover the overwhelming majority of how real WordPress sites actually get compromised. Run this pass quarterly, and treat any new plugin install or user addition as a reason to run it again sooner. Consistency, not sophistication, is what keeps most sites safe.