WordPress Security Hardening Checklist (2025)
WordPress security hardening checklist: file permissions, login protection, database hardening, and server-level controls from real agency deployments.
Most WordPress security guides tell you to install a plugin and call it done. That is not hardening - it is delegation. Real security comes from layered controls at the server, application, and code levels. This checklist covers all three.
Work through it in order. The earlier items are the highest-leverage: they prevent the most common attack vectors before any code runs.
Phase 1 - Server and hosting configuration
1. Use a managed host or configure your server correctly
The majority of successful WordPress compromises happen because of misconfigured servers, not vulnerable plugins. If you are on shared hosting, the risk is not just your own misconfiguration - it is other customers on the same server.
Managed hosts like Kinsta, WP Engine, and Cloudways run WordPress on isolated containers, apply server-level firewalls, and handle OS patching. If you run your own VPS:
- Keep the OS updated:
sudo apt-get update && sudo apt-get upgrade -y - Enable unattended security updates for Ubuntu:
sudo dpkg-reconfigure unattended-upgrades - Use a non-root sudo user, never log in as root
2. Set correct file permissions
WordPress files should not be world-writable. The correct permissions:
# Directories
find /var/www/html -type d -exec chmod 755 {} \;
# Files
find /var/www/html -type f -exec chmod 644 {} \;
# wp-config.php — more restrictive
chmod 600 /var/www/html/wp-config.php
The web server process (usually www-data) should own the files. WordPress needs write access to wp-content/ for media uploads and updates - this is fine. It should not need write access to core files.
3. Disable PHP execution in uploads
The uploads directory (wp-content/uploads/) should never execute PHP. An attacker who uploads a PHP file disguised as an image can execute arbitrary code unless you block it at the server level.
Nginx:
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
Apache .htaccess inside wp-content/uploads/:
<Files *.php>
deny from all
</Files>
4. Block xmlrpc.php
XML-RPC is a legacy API that is abused for brute-force credential stuffing and DDoS amplification. Unless you specifically need it (Jetpack, remote publishing), block it:
Nginx:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
Apache:
<Files xmlrpc.php>
deny from all
</Files>
Phase 2 - WordPress configuration
5. Lock down wp-config.php
Move wp-config.php one directory above the web root. WordPress checks the parent directory automatically. This prevents the file from being served even if Nginx/Apache misconfigures the PHP block:
mv /var/www/html/wp-config.php /var/www/wp-config.php
Add these constants inside it:
define( 'DISALLOW_FILE_EDIT', true ); // disable theme/plugin editor define( 'DISALLOW_FILE_MODS', true ); // disable plugin/theme installation from admin define( 'FORCE_SSL_ADMIN', true ); // admin always over HTTPS
DISALLOW_FILE_MODS is the nuclear option - use it on sites that are fully deployed and managed through Git/CI. It prevents auto-updates too.
6. Regenerate authentication keys and salts
These encrypt your auth cookies. If you migrated the site or have any suspicion of compromise, regenerate them immediately. Get fresh values from https://api.wordpress.org/secret-key/1.1/salt/ and replace the existing block in wp-config.php. This logs out all active sessions.
7. Change the database table prefix
The default wp_ prefix makes SQL injection attacks easier because attackers know your table names. Change it on new installs:
$table_prefix = 'agency7_';
On existing installs, use a plugin like Brozzme DB Prefix Change - it handles renaming the actual database tables and updating references in wp_options and wp_usermeta.
8. Delete unused admin accounts
Go to Users > All Users. Any account you did not create or recognise - delete it. After a compromise, attackers often create backdoor admin accounts. Check monthly.
9. Rename or block the default admin username
Creating a WordPress site with username admin gives attackers half the login credentials. If an admin account with this username exists, create a new admin with a different username, transfer all posts, then delete the admin account.
Phase 3 - Login and access control
10. Enforce strong passwords and limit login attempts
Use a plugin like Limit Login Attempts Reloaded or WP Cerber to rate-limit login attempts. Configure it to lock out an IP after 5 failed attempts for 30 minutes.
For the admin password itself: use a password manager to generate a 20+ character random string. The WordPress admin is the most valuable attack surface on the site.
11. Enable two-factor authentication
Every admin and editor account should require 2FA. WP 2FA (by Melapress) is the most reliable plugin for this. It supports TOTP apps (Google Authenticator, Authy), email codes, and backup codes.
Force 2FA for all user roles with admin capabilities:
// In functions.php - redirect non-2FA admins to setup
add_action( 'admin_init', function() {
if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
// WP 2FA provides a function to check setup status
if ( function_exists( 'wp_2fa_is_user_using_two_factor' ) &&
! wp_2fa_is_user_using_two_factor() ) {
wp_redirect( wp_2fa_get_setup_url() );
exit;
}
}
});
12. IP allowlist for wp-admin
If all your admins work from known IP addresses (office, home VPN), allowlist them at the server level:
Nginx:
location /wp-admin {
allow 203.0.113.0; # office
allow 198.51.100.0; # home
deny all;
}
location = /wp-login.php {
allow 203.0.113.0;
allow 198.51.100.0;
deny all;
}
This is the most effective possible login protection. Bots cannot even attempt a password.
13. Disable user enumeration
WordPress exposes usernames through the author archive (?author=1) and the REST API (/wp-json/wp/v2/users). Block these:
// Redirect author scan attempts
add_action( 'template_redirect', function() {
if ( is_author() && ! is_user_logged_in() ) {
wp_redirect( home_url(), 301 );
exit;
}
});
// Disable users endpoint in REST API for non-admins
add_filter( 'rest_endpoints', function( $endpoints ) {
if ( ! current_user_can( 'list_users' ) ) {
unset( $endpoints['/wp/v2/users'] );
unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
}
return $endpoints;
});
Phase 4 - Updates and maintenance
14. Keep everything updated
The single most important preventative measure: keep WordPress core, themes, and plugins updated. The majority of exploited vulnerabilities are in outdated plugin versions - often patched months before the attack.
Enable auto-updates for minor core versions:
define( 'WP_AUTO_UPDATE_CORE', 'minor' );
For plugins, auto-updates are risky on complex sites (a plugin update can break functionality). Instead, set up update monitoring and apply them weekly on a staging environment first.
15. Remove unused themes and plugins
Every inactive plugin is an attack surface even if it is deactivated. Deactivated plugins still have their files on disk and can be exploited if they contain file inclusion vulnerabilities. Delete what you do not use.
Keep only the active theme plus one default WordPress theme as a fallback.
16. Set up file change monitoring
WordPress security plugins like Wordfence and iThemes Security both offer file integrity monitoring - they alert you when core WordPress files change unexpectedly. A changed wp-settings.php or functions.php is a strong indicator of compromise.
On the server level, you can use AIDE or Tripwire for the same purpose.
Phase 5 - Backups and incident response
17. Automated off-site backups
A backup that lives on the same server as the site is not a backup - it gets compromised with the site. Use a plugin that ships backups to an external location:
- UpdraftPlus → Amazon S3, Google Drive, Dropbox, remote FTP
- WP Time Machine → S3
- ManageWP → offsite storage with one-click restore
Backup schedule: daily database, weekly files. Keep 30 days of retention.
18. Test restores
A backup you have never tested is a backup that might not work. Quarterly, restore to a staging environment and verify the site comes up correctly. Clients who discover their backups were corrupt during an actual incident are not clients for long.
19. Set up uptime and security monitoring
- UptimeRobot (free) - alerts you within 5 minutes of downtime
- Wordfence - real-time firewall, malware scanner, login protection
- WPScan - scheduled vulnerability scans against your site's installed plugins/themes
Quick-reference: priority order
If you can only do five things today:
- Set correct file permissions and block PHP in uploads
- Disable the file editor (
DISALLOW_FILE_EDIT) - Rate-limit login attempts
- Enable 2FA on all admin accounts
- Verify off-site backups exist and are recent
Security is not a one-time setup. Treat it as an ongoing process: monthly user audits, weekly update checks, quarterly backup restores.
Related reading
// new_articles
Get notified when new guides drop
Practical WordPress guides from a working agency owner. No filler. Unsubscribe any time.
Was this article helpful?
Thanks for the feedback!