Most WordPress breaches happen because someone skipped the boring steps. A default install is a open door: guessable usernames, permissive file modes, no rate limiting, and plugins that phone home without TLS. You can close that door in a few hours with ten concrete changes, no security degree required.
I've walked site owners through this exact sequence after their hosts suspended them for malware. The pattern is always the same—someone installed a nulled theme, left wp-admin wide open, or ran PHP 5.6 until the box got owned. This checklist stops that cycle. Follow it in order; each step builds on the last.
Step 1: Install a Security Plugin
Start with Wordfence (free tier is fine) or Solid Security (formerly iThemes Security). Both ship firewall rules, malware scanners, and login protection in one package. Install through the dashboard or wp-cli:
wp plugin install wordfence --activate
After activation, Wordfence runs a scan automatically. Let it finish—takes five to ten minutes on a typical site. Review the findings and fix high-severity issues first: outdated core files, weak passwords, world-writable directories. The plugin flags them with red icons; click through and apply the fix.
Don't install three security plugins at once. They conflict, especially around login throttling and firewall rules. Pick one, configure it fully, then move to the next step.
Step 2: Enforce Strong Passwords Site-Wide
WordPress lets users set "password" as a password if you don't stop them. Navigate to Settings → General in the dashboard, scroll to the membership section, and uncheck "Anyone can register" unless you actually need open registration. If you do need it, install a plugin like Password Policy Manager to enforce minimums: twelve characters, at least one number, one symbol.
For existing users, audit passwords manually. Go to Users → All Users, click into each account, scroll to "Account Management," and click "Send password reset." Force a reset for any account older than six months or tied to a ex-employee.
In my support queue, compromised user accounts cause more incidents than plugin CVEs. A contractor leaves, their password stays active, and six months later someone logs in from Bulgaria and drops a PHP shell in wp-content. Reset the passwords.
Step 3: Enable Two-Factor Authentication
Install the Two Factor Authentication plugin (search "two factor" in the plugin directory) or use Wordfence's built-in 2FA. Both support TOTP apps like Google Authenticator or Authy. After activating, go to Users → Your Profile and click "Set up two-factor authentication." Scan the QR code with your phone, enter the six-digit code, and save.
Enforce 2FA for all administrator and editor roles. Contributors and subscribers can skip it if your threat model allows. The Two Factor Authentication plugin has a settings page where you can require it per role. Check the boxes for Admin and Editor, save, then notify your team that they'll be prompted on next login.
Backup codes matter. Generate them during setup and store them in your password manager or a printed sheet in a safe. If you lose your phone, backup codes are the only way back into wp-admin without editing the database.
Step 4: Change the Default Database Prefix
Fresh WordPress installs use wp_ as the table prefix. Every SQL injection tool on GitHub knows that. Change it to something random during install, or rename it afterward with a plugin like iThemes Security (it has a one-click prefix changer under Advanced → Database Security).
If you prefer doing it manually, back up your database first:
wp db export backup-before-prefix-change.sql
Then edit wp-config.php and change this line:
$table_prefix = 'wp_';
to something like:
$table_prefix = 'wp_k9j2_';
Now run SQL commands to rename every table. For a standard install:
RENAME TABLE wp_commentmeta TO wp_k9j2_commentmeta;
RENAME TABLE wp_comments TO wp_k9j2_comments;
RENAME TABLE wp_links TO wp_k9j2_links;
-- repeat for all tables
Also update user meta and options that store the old prefix. Plugins like iThemes handle this automatically; manual migrations are tedious and error-prone. Use the plugin unless you have a reason not to.
Step 5: Disable File Editing in wp-admin
By default, WordPress lets administrators edit theme and plugin files directly through the dashboard. Convenient for quick fixes, terrible for security. If an attacker compromises an admin account, they can inject code into functions.php and own the site permanently.
Disable the editor by adding this line to wp-config.php, right above the line that says "That's all, stop editing":
define('DISALLOW_FILE_EDIT', true);
Save and reload wp-admin. The "Theme Editor" and "Plugin Editor" menu items disappear. Legitimate changes now require SFTP or SSH access, which means an attacker needs two separate credentials instead of one.
Step 6: Lock Down wp-config.php and .htaccess
These two files control everything. If someone reads wp-config.php, they get your database password. If they write to .htaccess, they can redirect traffic or upload shells.
Set restrictive permissions:
chmod 440 wp-config.php
chmod 644 .htaccess
Some hosts need chmod 400 on wp-config. Try 440 first; if the site breaks (white screen, database connection error), switch to 400. The .htaccess file needs to stay 644 because WordPress writes rewrite rules to it when you change permalink settings.
Move wp-config.php one directory above the web root if your host allows it. WordPress automatically looks one level up if it can't find the file in the install directory. This puts the database password outside the publicly accessible tree:
mv /home/user/public_html/wp-config.php /home/user/wp-config.php
Test the site afterward. If it loads normally, the move worked.
Step 7: Restrict wp-admin Access by IP (Optional but Recommended)
If you and your team log in from fixed IP addresses, block everyone else at the server level. Add this to the .htaccess file inside wp-admin/:
<Files admin-ajax.php>
Order allow,deny
Allow from all
</Files>
<Files async-upload.php>
Order allow,deny
Allow from all
</Files>
Order deny,allow
Deny from all
Allow from 203.0.113.5
Allow from 198.51.100.42
Replace the example IPs with your actual addresses. The admin-ajax.php and async-upload.php exceptions let front-end AJAX and media uploads work without breaking. Everything else in wp-admin becomes unreachable from other IPs.
Dynamic IPs make this impractical. Skip this step if you or your editors work from coffee shops or mobile connections. Use 2FA and login throttling instead.
Step 8: Enable Automatic Updates for Core and Plugins
WordPress updates itself for minor releases (5.9.1 to 5.9.2) but waits for approval on major ones (5.9 to 6.0). Force automatic updates for everything by adding these lines to wp-config.php:
define('WP_AUTO_UPDATE_CORE', true);
add_filter('auto_update_plugin', '__return_true');
add_filter('auto_update_theme', '__return_true');
This keeps the site patched without manual intervention. The downside: a bad plugin update can break the site at 3 AM. Mitigate that risk with daily backups (covered in the next step) and staging environments for high-traffic sites. For small sites, auto-updates prevent more problems than they cause.
Wordfence and other security plugins also have auto-update toggles in their settings. Enable them as a fallback if you don't want to edit wp-config.
Step 9: Set Up Daily Backups
Install UpdraftPlus or BackWPup. Both export the database and wp-content to remote storage: Dropbox, Google Drive, S3, or FTP. Configure it to run daily at a low-traffic hour (2 AM works for most zones) and retain seven days of history.
UpdraftPlus setup:
- Install and activate the plugin
- Go to Settings → UpdraftPlus Backups
- Click the Settings tab
- Choose a remote storage destination and authenticate
- Set schedule to "Daily" for files and database
- Retain 7 backups
- Click "Save Changes"
- Run a manual backup to test
Verify that backups actually arrive in your remote storage. I've seen dozens of UpdraftPlus installs where the credentials expired or the bucket was deleted, and nobody noticed until they needed a restore. Check your storage once a week for the first month.
Backups are useless if you can't restore them. Practice restoring to a staging domain or local environment at least once. UpdraftPlus has a one-click restore button; BackWPup requires manual database import and file extraction. Know the process before an emergency.
Step 10: Hide the WordPress Version and Login Hints
By default, WordPress advertises its version in meta tags, RSS feeds, and readme.html. Automated scanners use that to target known CVEs. Remove the version number by adding this to your theme's functions.php:
remove_action('wp_head', 'wp_generator');
Delete or rename readme.html and license.txt in the site root. They serve no purpose after install and leak version info.
WordPress also tells attackers whether a username exists when login fails. The error message says "Invalid username" or "Wrong password for username X," which lets someone enumerate valid accounts. Install a plugin like WPS Hide Login to replace the default login page URL (/wp-admin and /wp-login.php) with something custom like /login-k9j2. This stops scripted brute-force tools that blindly hit wp-login.php.
Configure Wordfence or Solid Security to limit login attempts: five tries per IP per hour is a reasonable starting point. After five failures, the IP gets blocked for twelve hours. Tune the numbers based on your user base—if legitimate users frequently mistype passwords, raise the limit to ten.
What If You Inherit a Compromised Site?
Sometimes you don't start from zero. You take over a client site that's already infected. In that case, run these checks before you touch anything:
- Scan with Wordfence or Sucuri SiteCheck to identify malware
- Check all user accounts for suspicious admins (Users → All Users)
- Review installed plugins and themes; delete anything nulled or abandoned
- Audit scheduled cron jobs:
wp cron event list - Check file permissions:
find . -type f -perm 0777
Clean the malware first, then apply the ten steps above. Applying security to an already-compromised site is like locking the door after the burglar is inside. Get them out, then bolt the door.
Common Questions
Do I need a paid security plugin?
Free tiers of Wordfence and Solid Security cover most small sites. Paid plans add real-time firewall updates, country blocking, and priority malware removal. If you run e-commerce or handle user data, the paid tier is worth it. Personal blogs can stay free.
Can I skip 2FA if I have a strong password?
No. Passwords leak in data breaches, get phished, or get keylogged. 2FA stops an attacker even if they have your password. It's the single highest-ROI security step on this list.
How often should I update plugins?
Weekly if you check manually, or enable auto-updates and let WordPress handle it. Outdated plugins are the most common entry point in every breach I've worked. Stay current.
What if I lose access to my 2FA device?
Use backup codes (generate them during setup) or connect via SFTP and rename the Two Factor Authentication plugin folder to disable it temporarily. Log in, reconfigure 2FA, then reactivate the plugin.
Should I use Cloudflare in addition to Wordfence?
Yes. Cloudflare blocks attacks at the edge before they hit your server. Wordfence protects the application layer. They complement each other. Enable "I'm Under Attack Mode" in Cloudflare during active DDoS events.
Your First 24 Hours
These ten steps take an afternoon to implement on a fresh site, longer on an inherited mess. Start with the security plugin and 2FA—they catch the most common attacks. Then work down the list during scheduled maintenance windows. After step ten, your WordPress install is no longer an easy target. Automated scanners move on to softer sites, and you sleep better knowing the basics are covered.
Schedule a monthly review: check plugin updates, rotate passwords every six months, audit user accounts quarterly, test a backup restore twice a year. Security is a habit, not a one-time checklist. But you have to build the foundation first, and that's what these ten steps give you.
