Most hacked WordPress sites aren’t hacked through WordPress core. They fall to outdated plugins, stolen or guessed passwords, and weak server setups. On a VPS you control every layer, so you can harden far beyond what a security plugin alone can do. Work through these steps roughly in order of impact.
Updates and inventory
1. Update everything promptly — core, plugins, themes, PHP. Enable automatic minor core updates (default) and consider auto-updates for well-maintained plugins. See WordPress automatic updates.
2. Remove what you don’t use. Deactivated plugins and unused themes still contain exploitable code. Delete them.
3. Avoid abandoned and nulled plugins. Plugins without updates for years, or “free” copies of premium plugins, are a common malware source.
Accounts
4. Strong, unique passwords for every account, and no username admin.
5. Two-factor authentication for all administrator and editor accounts — see WordPress 2FA.
6. Least privilege. Give users the lowest role that works (Author, Editor) — see WordPress user roles.
wp-config.php
7. Disable the built-in file editor:
define( 'DISALLOW_FILE_EDIT', true );
An attacker with an admin login can otherwise insert PHP code via Appearance → Theme File Editor.
8. Optionally block plugin/theme installs from the dashboard (manage them with WP-CLI or deployments instead):
define( 'DISALLOW_FILE_MODS', true );
This also disables updates from wp-admin, so only use it if you update via WP-CLI or CI.
9. Fresh salts and keys, and a non-default table prefix for new installs. Rotate salts after an incident: wp config shuffle-salts.
10. Protect the file itself: chmod 640 wp-config.php, owned by the deploy user with the web server’s group.
Files and permissions
11. Correct ownership and modes. Directories 755/750, files 644/640; never 777. Ideally the web server can write only to wp-content/uploads (and cache directories). See WordPress file permissions.
12. No PHP execution in uploads. Nginx:
location ~* /(?:uploads|files)/.*\.php$ { deny all; }
Uploaded web shells are useless if they can’t execute.
13. Block sensitive files:
location ~ /\.(?!well-known) { deny all; }
location ~* /(wp-config\.php|readme\.html|license\.txt|debug\.log)$ { deny all; }
Login and XML-RPC
14. Rate-limit or protect wp-login.php — Nginx rate limiting, Fail2Ban or CrowdSec. See Nginx rate limiting and Fail2Ban for WordPress.
15. Disable XML-RPC if you don’t use Jetpack or the mobile app — it allows many password guesses per request. See disabling XML-RPC.
16. Optionally restrict /wp-admin by IP for sites with few editors (allow admin-ajax.php, which the front end may use).
Server level
17. Isolate sites. On a server with several sites, give each its own PHP-FPM pool running as its own Linux user, so a compromise of one site can’t modify the others.
; /etc/php/8.3/fpm/pool.d/example.conf
[example]
user = example
group = example
listen = /run/php/php8.3-fpm-example.sock
listen.owner = www-data
listen.group = www-data
php_admin_value[open_basedir] = /var/www/example.com:/tmp
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen
Test that plugins still work after restricting functions.
18. HTTPS everywhere and security headers — see WordPress SSL and security headers.
19. Harden the server itself — SSH keys, firewall, automatic security updates. See the Linux server hardening checklist.
Recovery
20. Backups you’ve tested — database and files, daily, stored off the server, with several days of history. When something goes wrong, a clean backup is the fastest fix. See WordPress backups without plugins.
Monitoring for compromise
wp core verify-checksumsandwp plugin verify-checksums --alldetect modified files.- Watch for new admin users (
wp user list --role=administrator) and unexpected files in uploads:
find /var/www/example.com/wp-content/uploads -name "*.php"
find /var/www/example.com -name "*.php" -mtime -2
- An external uptime monitor with a keyword check catches defacements and blank pages.
If you find signs of a hack, follow WordPress malware removal.
What about security plugins?
Plugins like Wordfence or Sucuri add firewalls, scanning and login protection — useful, especially where you can’t change server config. On a VPS, server-level measures (Nginx rules, rate limiting, PHP isolation, Fail2Ban) are faster and can’t be bypassed by PHP-level exploits. Many people combine both. See Sucuri vs Wordfence.
A WordPress VPS gives you root access to apply every measure on this list.