Every WordPress server I deploy gets attacked before I finish configuring it. That is not drama — open a fresh EC2 instance's auth log an hour after boot and it is already full of strangers trying root and admin, because credential-stuffing bots scan the entire IPv4 space continuously. Running a security venture taught me to treat that background radiation as the design constraint: the doors attackers actually try on a WordPress box are predictable — SSH, the admin login, unencrypted traffic, and stale plugins — so hardening all four in one sitting covers the real attack surface. This is the exact sequence I run on every WordPress server I stand up.

Start with SSH, because everyone else does
Three changes end the brute-force careers in your auth log. In /etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin no
Keys-only means there is no password to stuff; no root login means the one username every bot tries first is a dead end, and you work through a sudo-capable user instead. The third change lives in the security group, not the config file: port 22 open to your IP or VPN range, never 0.0.0.0/0. An attacker who cannot reach the port cannot brute-force it.
Then the habit that separates people who have done this from people following a checklist: reload the daemon (sudo systemctl reload sshd) and — before closing your session — open a second terminal and confirm you can still log in. The oldest hardening joke is locking yourself out with the door you just reinforced, and it stops being funny around the third time. Keep the old session alive until the new one works.
While you are here, patch the daemon itself. The OpenSSH 9.3p2 release existed specifically to fix a remote-code-execution vulnerability in a forwarding path — a reminder that the service you harden is also software that needs updating. sudo dnf update openssh-server belongs in the same sitting as the config changes.
Lock down the WordPress admin
The login page is the second-favorite target, and the fixes stack:
- Delete any user named
adminafter transferring its content to a new account. Half of credential stuffing is guessing the username; do not donate it. - Two-factor authentication via any reputable plugin, and limit login attempts so stuffing gets rate-limited into futility.
- If your team's IPs are stable, restrict
/wp-adminat the web server level. This is the bluntest control and the most effective one — attackers cannot brute-force a page they cannot reach.
Two lines in wp-config.php complete the lockdown:
define('DISALLOW_FILE_EDIT', true);
This removes the built-in theme and plugin code editor. Without it, one compromised admin account is instant PHP execution on your server — the editor is the single most common pivot from "stolen password" to "owned box." And regenerate your salts from the official WordPress secret-key generator; fresh salts invalidate every stolen session cookie in circulation.
File permissions matter here too — WordPress needs to write to uploads/ but should never own more than it needs. I covered the permission model that fixes both update errors and over-permissive ownership in my plugin-update permissions fix on EC2.
TLS is the floor, not the finish
Install a Let's Encrypt certificate and force HTTPS site-wide with a 301 redirect — my full walkthrough for Apache on Amazon Linux is in the SSL certificate setup guide. Then go past the green padlock:
- Set
Strict-Transport-Securityso browsers refuse to downgrade even on a first visit typo. - Update the WordPress site URL settings to
https://so links generate natively instead of leaning on the redirect forever. - Verify with an external scan — SSL Labs flags weak protocols and incomplete chains the padlock quietly hides.
- Test renewal with a dry run (
sudo certbot renew --dry-run). An expired certificate takes your credibility down with your uptime, and it always expires on a weekend.
Scan yourself before they scan you
WPScan enumerates exactly what attackers enumerate — vulnerable plugin versions, exposed usernames, readable config files:
wpscan --url https://your-site.com --enumerate vp,u --api-token YOUR_TOKEN
The free API tier is enough for your own sites. Run it quarterly and after every plugin change, and let the findings drive the unglamorous work that actually prevents breaches: updating the plugin you forgot about, removing the theme you trialed once in 2023, closing user enumeration. Pair it with a monthly patch day — core, plugins, and system packages together, staged first if the site earns money. Almost every hacked WordPress site I have been called in on traced back to a known, patched vulnerability that simply had not been applied.
The firewall layer most guides skip
Security groups guard the network edge; a web application firewall guards the application edge, and WordPress specifically benefits. Cloudflare's free tier in front of the instance filters the bulk of exploit scanning before it touches Apache, hides your origin IP, and absorbs the small-scale denial-of-service noise every WordPress site eventually attracts.
The step that makes it real: after pointing DNS through the proxy, restrict your security group's HTTP/HTTPS ingress to Cloudflare's published IP ranges. Skip that and attackers who find your origin IP simply walk around the firewall. With it, they lose the ability to even address your server directly. Fifteen minutes of setup, permanent reduction in attack surface.
The backup that makes everything survivable
Hardening reduces the odds; backups cap the damage. Snapshot the EBS volume daily, keep at least a week of history, and store database dumps off-instance — a backup living on the server it protects vanishes with the server. Then do the thing almost nobody does: restore one backup, once, onto a scratch instance. A restore you have never rehearsed is a hypothesis, not a plan, and the middle of an incident is the worst possible moment to test a hypothesis.
Know the compromise symptoms so you catch them early: admin users you did not create, unfamiliar PHP files in uploads/, cron entries running base64 blobs, traffic to URLs that do not exist, or Google flagging the site. If any appear, do not clean in place — snapshot for forensics, restore last-known-good, rotate every credential, and only then diagnose the entry point. That discipline is the difference between the recoveries I have run, like the critical-error case study, and the sites that get re-compromised a week after "cleanup."
The checklist, compressed
For your runbook, six lines that cover the attacks WordPress servers actually face:
- SSH keys-only, root disabled, port 22 scoped to known IPs, OpenSSH patched
- Admin behind 2FA, attempts limited, no
adminuser,DISALLOW_FILE_EDITon - TLS forced with HSTS, renewal dry-run tested
- WPScan quarterly, patch day monthly
- Daily snapshots, dumps off-instance, one rehearsed restore
- Cloudflare proxying, origin ingress restricted to Cloudflare ranges
Print it, run it on every new instance, and re-run it quarterly — plus after any incident or major change. Hardening decays because software moves: keys that should die, admin accounts that outlived their humans, security group rules referencing IPs nobody remembers. A server hardened once is briefly safe. A server on a maintenance rhythm stays that way.
Hardening client stacks and keeping them on that rhythm is one of the services I provide — if you would rather hand over the checklist than run it, my services page covers how I take that on.