================================================================================
WORDPRESS HARDENING TECHNICAL CHECKLIST
================================================================================
Author: Ben Huffman / Dirt River Design
Target: CentOS 7.9 / cPanel 110 / Apache 2.4 / Virtuozzo
Purpose: Make your WordPress site more expensive to compromise
This is not security theater. This is defense-in-depth.
The goal is not to make your site "unhackable" (nothing is). The goal is to
raise the time and cost required to compromise your site so automated attacks
move on to easier targets.
Every layer you add increases attacker cost. Most bots will move on.
================================================================================
SECTION 1: WHM (WEB HOST MANAGER) LEVEL BLOCKS
================================================================================
These blocks apply to ALL accounts on your server. Do this if you manage
multiple WordPress sites on dedicated or VPS hosting.
Login to WHM as root.
1.1 - MODSECURITY CONFIGURATION
--------------------------------------------------------------------------------
WHM > Security Center > ModSecurity Configuration
Enable ModSecurity: ON
Enable OWASP ModSecurity Core Rule Set: ON
This provides baseline attack detection for:
- SQL injection attempts
- XSS attacks
- Remote file inclusion
- Known malicious user agents
1.2 - MODSECURITY CUSTOM RULES (WORDPRESS-SPECIFIC)
--------------------------------------------------------------------------------
WHM > Security Center > ModSecurity Tools > Rule List > Add New Rule
Add these custom rules one at a time:
# Block WordPress xmlrpc.php brute force attacks
SecRule REQUEST_URI "@endsWith xmlrpc.php" \
"id:10001,phase:1,deny,status:403,nolog,msg:'Blocked xmlrpc.php access'"
# Count only POSTs to wp-login.php (actual login submits)
SecRule REQUEST_METHOD "@streq POST" \
"id:10002,phase:1,pass,nolog,chain,setvar:ip.wp_login_counter=+1,expirevar:ip.wp_login_counter=60"
SecRule REQUEST_URI "@endsWith wp-login.php"
# Block after 5 POSTs in 60 seconds per IP
SecRule IP:WP_LOGIN_COUNTER "@gt 5" \
"id:10003,phase:1,deny,status:429,nolog,msg:'wp-login rate limit exceeded'"
# Block REST API user enumeration
SecRule REQUEST_URI "@beginsWith /wp-json/wp/v2/users" \
"id:10004,phase:1,deny,status:403,nolog,msg:'Blocked REST user enumeration'"
# Block author enumeration via ?author= parameter
SecRule QUERY_STRING "@rx (^|&)author=\d+(&|$)" \
"id:10005,phase:1,deny,status:403,nolog,msg:'Blocked author enumeration scan'"
After adding rules, click "Restart ModSecurity" to activate.
1.3 - CSFLAGS (CONFIGSERVER SECURITY & FIREWALL)
--------------------------------------------------------------------------------
If you have CSF installed (common on cPanel servers):
WHM > Plugins > ConfigServer Security & Firewall
Edit Firewall Configuration:
# Block countries with highest automated attack traffic
# Only do this if your business is US-only and you don't travel
CC_DENY = "CN,RU,UA,VN,IN,PK,BR,ID,NG,BD"
# Allow your country
CC_ALLOW_FILTER = "US"
# Enable connection tracking
CT_LIMIT = "100" # Max connections per IP
# Enable port scan detection
PS_INTERVAL = "30"
PS_LIMIT = "10"
# Enable login failure detection (cPanel/WHM/SSH)
LF_TRIGGER = "5" # Failed logins before temp block
LF_TRIGGER_PERM = "10" # Failed logins before perm block
Click "Change" then "Restart csf+lfd"
CAUTION: Country blocking will lock you out if you travel or use VPN.
Test before enabling on production.
1.4 - DISABLE DANGEROUS PHP FUNCTIONS SERVER-WIDE
--------------------------------------------------------------------------------
WHM > MultiPHP INI Editor > System PHP-FPM (or your PHP handler)
Find: disable_functions
Add these to the list (comma separated):
exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,phpinfo
This breaks remote code execution exploits. Some plugins may complain.
If a legitimate plugin breaks, selectively re-enable only what's needed.
Save changes.
================================================================================
SECTION 2: CPANEL ACCOUNT LEVEL BLOCKS
================================================================================
These apply to individual cPanel accounts. Use if you host a single WordPress
site or want per-account control.
Login to cPanel for the account hosting WordPress.
2.1 - IP BLOCKER (MANUAL BLOCK LIST)
--------------------------------------------------------------------------------
cPanel > Security > IP Blocker
Add known malicious IP ranges manually if you see repeat attacks in logs.
Example: If you see brute force from 185.220.101.x range repeatedly:
Add: 185.220.101.0/24
This blocks the entire /24 subnet (256 IPs).
Check your Apache access logs regularly:
/home/USERNAME/access-logs/DOMAIN.com
2.2 - HOTLINK PROTECTION
--------------------------------------------------------------------------------
cPanel > Security > Hotlink Protection
Enable: YES
URLs to allow: (leave blank or add your own CDN domain if you use one)
Block extensions: jpg,jpeg,gif,png,bmp,webp,svg,mp4,pdf
This prevents other sites from embedding your images and consuming bandwidth.
Not directly security-related but prevents resource abuse.
2.3 - LEECH PROTECTION
--------------------------------------------------------------------------------
cPanel > Security > Leech Protection
Enable for your WordPress login accounts if you have multiple site editors.
This detects if a single WordPress account is being accessed from multiple
IPs simultaneously (credential sharing or compromise).
2.4 - DIRECTORY PRIVACY (PASSWORD PROTECT wp-admin)
--------------------------------------------------------------------------------
cPanel > Files > Directory Privacy
Navigate to: public_html/wp-admin
Click "Edit"
Enable: "Password protect this directory"
Name: "WordPress Admin"
Create a user:
Username: (anything - does not need to match WordPress username)
Password: (strong random password)
This adds HTTP Basic Auth BEFORE WordPress login loads. Attackers must know
two sets of credentials. This blocks most brute force bots immediately.
IMPORTANT: You will need to whitelist the admin-ajax.php file or your site
will break. See Section 3.3 for .htaccess rules.
================================================================================
SECTION 3: .HTACCESS FILE HARDENING
================================================================================
Edit your .htaccess file in WordPress root directory via:
- cPanel File Manager
- FTP client
- SSH with nano or vim
Add these rules AT THE TOP of your .htaccess file, BEFORE the WordPress
rewrite rules.
3.1 - COMPLETE .HTACCESS HARDENING SNIPPET
--------------------------------------------------------------------------------
# BEGIN WordPress Hardening
# Added: 2026-01-11
# Purpose: Block enumeration, brute force, and common exploits
# --------------------------------------------------
# Protect .htaccess and other sensitive files
# --------------------------------------------------
Require all denied
# --------------------------------------------------
# Block xmlrpc.php (remote publishing / pingback attacks)
# Only disable if you use Jetpack or WordPress mobile app
# --------------------------------------------------
Require all denied
# --------------------------------------------------
# Block REST API user enumeration endpoint
# Disable if you run headless WordPress
# --------------------------------------------------
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/wp-json/wp/v2/users [NC]
RewriteRule ^ - [F,L]
# --------------------------------------------------
# Block ?author= user enumeration scans
# Disable if you run multi-author blog with public profiles
# --------------------------------------------------
RewriteEngine On
RewriteCond %{QUERY_STRING} ^author=([0-9]*) [NC]
RewriteRule ^ - [F,L]
# --------------------------------------------------
# Block access to wp-includes folder (except allowed files)
# --------------------------------------------------
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
# --------------------------------------------------
# Block WordPress version detection
# --------------------------------------------------
Require all denied
# --------------------------------------------------
# Disable directory browsing
# --------------------------------------------------
Options -Indexes
# --------------------------------------------------
# Block suspicious user agents (known bad bots)
# --------------------------------------------------
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (sqlmap|nikto|masscan|nmap|openvas|acunetix|nessus|grabber|dirbuster) [NC]
RewriteRule .* - [F,L]
# --------------------------------------------------
# Block requests with suspicious query strings
# Common SQL injection and RFI patterns
# --------------------------------------------------
RewriteEngine On
RewriteCond %{QUERY_STRING} (eval\(|base64_decode|gzip|deflate) [NC,OR]
RewriteCond %{QUERY_STRING} (UNION.*SELECT|INSERT.*INTO|DELETE.*FROM|SELECT.*FROM) [NC,OR]
RewriteCond %{QUERY_STRING} (localhost|127\.0\.0\.1|loopback) [NC,OR]
RewriteCond %{QUERY_STRING} (