You’ve built the site. The design looks great, the content is ready, and your client is excited to go live. But before you hit publish — have you actually secured it?
WordPress powers 43% of all websites globally, making it the most lucrative target for cybercriminals. In 2025 alone, 7,966 new vulnerabilities were discovered in the WordPress ecosystem — a 34% increase from the previous year. Most of those vulnerabilities could have been closed in under an hour with the right checklist.
This is that checklist.
We’ve helped hundreds of businesses — law firms, eCommerce stores, real estate agencies — launch and maintain WordPress sites that don’t get hacked. Here are the exact 25 steps we run through every single time, in the order we do them.
Before We Start: Two Things to Understand
Security is not a plugin. Installing Wordfence doesn’t mean your site is secure. Security is a configuration discipline. A plugin is a monitoring tool, not a substitute for proper setup.
Most hacks are automated. Most people think hackers personally target their site. They don’t. Automated bots crawl the web 24/7, scanning for known vulnerabilities. If your site has one, they’ll find it. This means even a brand new site with no traffic is at risk the moment it goes live.
With that said, here’s the complete checklist.
Table of Contents
- Critical — Do These First
- High Priority — Complete Before Going Live
- 6. Add All Critical HTTP Security Headers
- 7. Disable Directory Listing
- 8. Protect wp-config.php
- 9. Remove the WordPress Version Number from Page Source
- 10. Disable XML-RPC (Unless You Need It)
- 11. Restrict Access to wp-login.php
- 12. Block User Enumeration
- 13. Restrict the REST API User Endpoint
- 14. Set Correct File Permissions
- 15. Protect the debug.log File
- Medium Priority — Important, Do Within First Week
- Low Risk But Worth Doing
- Pre-Launch Security Checklist — Quick Reference
- After Launch: Keep It Secure
Critical — Do These First
These five items account for the majority of successful WordPress hacks. No other steps matter if these aren’t done first.
1. Force HTTPS Site-Wide
If your site is still loading over HTTP — even partially — visitor data is exposed to interception on every page. SSL is also a Google ranking factor sites without it are actively penalised.
How to do it:
- Issue a free SSL certificate from your hosting control panel (cPanel → Let’s Encrypt, or Cloudflare’s free plan)
- In WordPress Admin → Settings → General, change both URL fields from http:// to https://
- Add this redirect to your .htaccess file:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]- Run a search-replace in your database. With WP-CLI: wp search-replace ‘http://yourdomain.com’ ‘https://yourdomain.com’ –skip-columns=guid
Check: Your browser padlock should show “Connection is secure.” No mixed-content warnings.
2. Change the Default Admin Username
A 2025 study by Sucuri found that 12% of compromised WordPress sites used the username “admin” with passwords shorter than 8 characters. The “admin” username is the first thing every brute-force tool tries. Remove it.
How to do it:
- Create a new Administrator account with a unique, non-obvious username (not your name, not your domain)
- Log out, log in as the new account
- Delete the original “admin” account, reassigning its content to your new account
- Update your display name: Users → Edit → Display Name Publicly As → choose something generic like “NinjaSofts Team”
3. Use Strong, Unique Passwords + Password Manager
Every administrator and editor account needs a password that is at least 20 characters, randomly generated, and unique to this site. Reused passwords mean a breach on any other site can unlock your WordPress admin.
Tools to use: 1Password, Bitwarden (free), or LastPass. Use their password generator — never create passwords manually.
For extra protection: Install WP 2FA (free plugin) and enable two-factor authentication on all admin accounts. 2FA blocks 99.9% of automated attacks — even with compromised passwords.
4. Keep WordPress Core, Plugins, and Themes Updated
According to Patchstack’s 2025 annual report, 97% of WordPress vulnerabilities originated in plugins and themes, not WordPress core. Every outdated plugin is a known, documented attack vector.
Before launch:
- Update WordPress core to the latest version
- Update all installed plugins
- Update your active theme and any parent theme
- Delete any plugins or themes you are not using — deactivated plugins still represent attack surface
Enable auto-updates for minor releases: In wp-config.php , add:
define( 'WP_AUTO_UPDATE_CORE', true );5. Run Automated Daily Backups to Off-Site Storage
A backup stored on the same server as your WordPress installation is not a real backup. If the server is compromised, the backup goes with it.
The correct setup:
- Plugin: UpdraftPlus (free) or BlogVault
- Storage: Google Drive, Dropbox, or Amazon S3 — never “same server”
- Frequency: Daily for active sites, weekly minimum for low-traffic sites
- Retention: Keep at least 30 days of backup history
Test a restore before launch. A backup you’ve never restored is a backup you’ve never verified.
High Priority — Complete Before Going Live
6. Add All Critical HTTP Security Headers
Security headers are instructions your server sends to the browser that activate protective policies. Without them, your visitors’ browsers have no guidance on how to handle cross-origin requests, clickjacking attempts, or MIME-type sniffing attacks.
Add these to your .htaccess file inside <IfModule mod_headers.c> :
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosnif
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-XSS-Protection "1; mode=block"
</IfModule>Verify with securityheaders.com — aim for an A or A+ rating.
7. Disable Directory Listing
By default, if a directory on your server doesn’t have an index.php file, the web server displays a browsable list of all files in that folder. This means anyone can visit /wp-content/uploads/ and see every file you’ve ever uploaded.
Fix — add one line to .htaccess :
Options -IndexesAlso create a blank index.php file in /wp-content/uploads/ and /wp-content/plugins/ as a secondary measure.
Test: Visit https://yourdomain.com/wp-content/uploads/ — it should return a 403 Forbidden error, not a file list.
8. Protect wp-config.php
wp-config.php contains your database credentials, authentication secret keys, and table prefix. If an attacker can read this file directly, they have full control of your database.
Add to .htaccess :
<Files wp-config.php>
Order Allow,Deny
Deny from all
</Files>Also move wp-config.php one directory above your WordPress root — WordPress natively supports this and the web server won’t serve files above the root.
9. Remove the WordPress Version Number from Page Source
Outdated software accounts for 96% of WordPress vulnerabilities. Exposing your version number tells automated scanners exactly which CVEs to try. Remove it.
Add to functions.php :
// Remove version from head and feeds
remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );
// Remove version from scripts and stylesheets
add_filter( 'style_loader_src', function( $src ) {
return remove_query_arg( 'ver', $src );
});
add_filter( 'script_loader_src', function( $src ) {
return remove_query_arg( 'ver', $src );
});Verify: View source on your homepage and search for “generator” — the tag should be gone.
10. Disable XML-RPC (Unless You Need It)
XML-RPC is a legacy WordPress API. Unless you specifically use Jetpack or post from a mobile app, it should be disabled. It allows attackers to attempt hundreds of password combinations per single HTTP request via the multicall method.
Add to .htaccess :
<Files xmlrpc.php>
Order Allow,Deny
Deny from all
</Files>If you use Jetpack, disable only multicall by adding to functions.php :
add_filter( 'xmlrpc_methods', function( $methods ) {
unset( $methods['system.multicall'] );
return $methods;
});11. Restrict Access to wp-login.php
The WordPress login page is the most attacked URL in any WordPress installation. Bots start hammering it within hours of a site going live.
Option A — IP restriction:
<Files wp-login.php>
Order Deny,Allow
Deny from all
Allow from YOUR.STATIC.IP.HERE
</Files>Option B: Install WPS Hide Login (free plugin) and move the login URL to something non-standard like /team-access/ . Bots will never find it.
Option C: Add Google reCAPTCHA v3 to the login form using WPForms or Fluent Forms.
Combine at least two of these approaches.
12. Block User Enumeration
Visiting /?author=1 on an unprotected WordPress site redirects to /author/USERNAME/ , revealing your admin’s login name. This cuts an attacker’s work in half.
Add to functions.php :
add_action( 'template_redirect', function() {
if ( is_author() ) {
wp_redirect( home_url(), 301 );
exit;
}
});13. Restrict the REST API User Endpoint
The WordPress REST API exposes your admin username at /wp-json/wp/v2/users/1 by default. Restrict it.
add_filter( 'rest_endpoints', function( $endpoints ) {
if ( isset( $endpoints['/wp/v2/users'] ) ) {
unset( $endpoints['/wp/v2/users'] );
}
if ( isset( $endpoints['/wp/v2/users/(?P<id>[\\d]+)'] ) ) {
unset( $endpoints['/wp/v2/users/(?P<id>[\\d]+)'] );
}
return $endpoints;
});14. Set Correct File Permissions
Incorrect file permissions are one of the most common misconfigurations on WordPress sites, especially on shared hosting.
Correct permissions:
- Files: 644 (owner can read/write, everyone else can read)
- Directories: 755 (owner can read/write/execute, everyone else can read/execute)
- wp-config.php : 400 or 440 (read-only for owner only)
Set via SSH/Terminal:
find /path/to/wordpress -type f -exec chmod 644 {} \;
find /path/to/wordpress -type d -exec chmod 755 {} \;
chmod 400 /path/to/wordpress/wp-config.phpNever set files or directories to 777. If a plugin tells you to set 777 permissions, find a different plugin.
15. Protect the debug.log File
If you’ve ever had WP_DEBUG_LOG enabled, there may be a debug.log file in /wp-content/ that is publicly readable. This file often contains database table names, file paths, and plugin errors.
Check: Visit https://yourdomain.com/wp-content/debug.log — if you see content instead of a 404, this is exposed.
Fix — add to .htaccess in /wp-content/ :
<Files "debug.log">
Order Allow,Deny
Deny from all
</Files>Then disable debug logging in wp-config.php :
define( 'WP_DEBUG', false );
define( 'WP_DEBUG_LOG', false );Medium Priority — Important, Do Within First Week
16. Change the WordPress Database Table Prefix
The default WordPress table prefix is wp_ . Every automated SQL injection script knows this. Changing it removes the lowest-hanging fruit.
This is easiest to configure during a fresh install. On an existing site, use a plugin like Better WP Security — always take a full database backup first.
17. Disable PHP Execution in Upload Directories
Even if an attacker uploads a malicious PHP file to your uploads directory, PHP execution should be blocked so the file can never run.
Create a .htaccess file in /wp-content/uploads/ containing:
<Files *.php>
Deny from all
</Files>WordPress session and authentication cookies should be transmitted securely and protected from JavaScript access. Without these flags, XSS attacks can steal session cookies.
Add to wp-config.php :
@ini_set( 'session.cookie_httponly', 1 );
@ini_set( 'session.cookie_secure', 1 );
@ini_set( 'session.cookie_samesite', 'Strict' );For Apache, enforce globally via .htaccess :
<IfModule mod_headers.c>
Header always edit Set-Cookie "^(.*)" "$1; HttpOnly; Secure; SameSite=Strict"
</IfModule>19. Limit Login Attempts
Without login rate limiting, bots can attempt thousands of passwords per hour against your login page. Implement a limit after which an IP is temporarily blocked.
Without a plugin — add to functions.php :
add_filter( 'authenticate', function( $user, $username, $password ) {
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'login_attempts_' . md5( $ip );
$attempts = (int) get_transient( $key );
if ( $attempts >= 5 ) {
return new WP_Error( 'too_many_attempts', 'Too many login attempts. Try again in 15 minutes.' );
}
if ( is_wp_error( $user ) ) {
set_transient( $key, $attempts + 1, 15 * MINUTE_IN_SECONDS );
}
return $user;
}, 30, 3 );With a plugin: Limit Login Attempts Reloaded (free, 2M+ installs).
20. Replace Exposed Email Addresses with a Contact Form
Plain mailto: links in your page’s HTML source are harvested by spam bots. Replace them with a contact form.
Free plugins: WPForms Lite, Contact Form 7, Fluent Forms.
21. Implement a Web Application Firewall (WAF)
A WAF sits between your visitors and your server, analysing incoming traffic and blocking malicious requests before they reach WordPress.
- Cloudflare (free tier): DNS-level WAF. Stops attacks before they reach your server. Easiest to implement.
- Wordfence (free): Endpoint WAF installed on your server. Excellent for shared hosting.
- Sucuri (paid): Cloud-based WAF with a strong malware removal guarantee.
22. Regenerate WordPress Security Keys and Salts
Security keys and salts encrypt WordPress authentication cookies. Regenerating them invalidates all active sessions — including any attacker sessions that may exist.
Generate new keys at: api.wordpress.org/secret-key/1.1/salt/
Replace the corresponding lines in wp-config.php . Do this before launch and after any suspected compromise.
Low Risk But Worth Doing
23. Update Your PHP Version
If you’re running anything older than PHP 8.2, you’re operating on an expired engine. PHP 7.4 reached its end-of-life on November 28, 2022.
Check: WordPress Admin → Tools → Site Health → Info → Server.
Update via your hosting control panel. Most managed hosts (Cloudways, Kinsta, WP Engine) allow PHP version changes with one click.
24. Remove the Server Version Header
The HTTP Server header often reveals the web server software and version — e.g., Apache/2.4.51 . This tells automated scanners which known exploits to try.
For Apache — add to .htaccess :
ServerTokens Prod
ServerSignature OffFor Nginx — add to nginx.conf :
server_tokens off;25. Set Up Security Monitoring and Alerts
A hardened site still needs to be watched. Enable monitoring so you know immediately if something changes.
What to monitor:
- File integrity monitoring (Wordfence or iThemes Security)
- Failed login attempts
- New admin user creation
- Plugin or theme file changes
- Blacklist/malware status (Sucuri’s free external scanner runs weekly) — visit sitecheck.sucuri.net
Google Search Console will email you if Google detects malware or deceptive content on your site — make sure it’s set up and your email is current.
Pre-Launch Security Checklist — Quick Reference
CRITICAL
- SSL forced site-wide, HTTPS confirmed, no mixed content
- Admin username changed from “admin” to unique username
- Strong passwords + 2FA on all admin accounts
- Core, plugins, and themes all updated — unused ones deleted
- Daily automated backups to off-site storage, test restore confirmed
HIGH PRIORITY
- HTTP security headers added (X-Frame-Options, HSTS, CSP, etc.)
- Directory listing disabled (Options -Indexes)
- wp-config.php protected and file permissions set correctly
- WordPress version removed from page source
- XML-RPC disabled or multicall method blocked
- wp-login.php protected (IP restriction, renamed URL, or CAPTCHA)
- User enumeration via /?author=1 blocked
- REST API user endpoint restricted
- debug.log file blocked and debug mode disabled
MEDIUM PRIORITY
- Database table prefix changed from wp_
- PHP execution disabled in /wp-content/uploads/
- Cookie security flags set (HttpOnly, Secure, SameSite)
- Login attempt limiting enabled
- Mailto: links replaced with contact forms
- Web Application Firewall (WAF) configured
- WordPress security keys and salts regenerated
LOW RISK
- PHP version is 8.2 or newer
- Server version header suppressed
- Security monitoring and alerts configured
After Launch: Keep It Secure
Security isn’t a one-time task. It’s a system you maintain. Set a monthly reminder to:
- Update all plugins, themes, and WordPress core
- Review user accounts and remove any you don’t recognise
- Confirm your backup restore works
- Review your security plugin’s log for unusual activity
Check Your Score Before You Launch
Our free WordPress Security Scanner runs 14 of these checks automatically — SSL, security headers, login page exposure, directory listing, user enumeration, debug log access, REST API exposure, XML-RPC status, cookie security flags, and more — and returns a scored report with step-by-step remediation for every issue it finds.





