================================================================================ 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} ( # -------------------------------------------------- # Block access to wp-config.php (redundant but important) # -------------------------------------------------- Require all denied # -------------------------------------------------- # Protect uploads directory from executing PHP # Prevents malicious file uploads from running # -------------------------------------------------- Require all denied # -------------------------------------------------- # Force HTTPS (if you have SSL certificate installed) # -------------------------------------------------- RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # END WordPress Hardening 3.2 - IF YOU USE HTTP BASIC AUTH ON wp-admin -------------------------------------------------------------------------------- If you password-protected wp-admin via cPanel Directory Privacy (Section 2.4), you MUST whitelist admin-ajax.php or your site will break. Add this AFTER the hardening snippet: # -------------------------------------------------- # Allow admin-ajax.php to bypass HTTP Basic Auth # Required if wp-admin is password protected # -------------------------------------------------- Satisfy Any Order allow,deny Allow from all Require all granted 3.3 - TESTING YOUR .HTACCESS CHANGES -------------------------------------------------------------------------------- After adding rules: 1. Load your site in browser - should work normally 2. Try to access: yourdomain.com/xmlrpc.php - should see 403 Forbidden 3. Try to access: yourdomain.com/wp-json/wp/v2/users - should see 403 Forbidden 4. Try to access: yourdomain.com/?author=1 - should see 403 Forbidden 5. Try to login to wp-admin - should work normally (or prompt for HTTP auth first) If your site breaks: - Via cPanel File Manager, edit .htaccess - Comment out rules one section at a time with # until you find the conflict - Check Apache error log: /home/USERNAME/logs/DOMAIN.com-error_log ================================================================================ SECTION 4: WORDPRESS wp-config.php HARDENING ================================================================================ Edit wp-config.php in your WordPress root directory. Add these constants BEFORE the line that says: /* That's all, stop editing! Happy publishing. */ 4.1 - DISABLE FILE EDITING VIA DASHBOARD -------------------------------------------------------------------------------- /** Disable theme/plugin editor in WordPress admin */ define('DISALLOW_FILE_EDIT', true); This prevents attackers from modifying theme files through the WordPress dashboard if they compromise an admin account. Forces file changes via FTP/SSH. 4.2 - DISABLE PLUGIN/THEME INSTALLATION -------------------------------------------------------------------------------- /** Disable plugin and theme installation/updates via admin */ define('DISALLOW_FILE_MODS', true); This prevents malicious plugin installation if an attacker gets admin access. WARNING: This also disables legitimate updates. Only use if you manage updates via WP-CLI or manually via FTP. Alternative (less restrictive): /** Allow updates but disable installation */ define('DISALLOW_FILE_MODS', false); define('DISALLOW_FILE_EDIT', true); 4.3 - LIMIT POST REVISIONS -------------------------------------------------------------------------------- /** Limit post revisions (reduces database bloat and attack surface) */ define('WP_POST_REVISIONS', 3); Or disable entirely: define('WP_POST_REVISIONS', false); Attackers can hide malicious code in old post revisions. Limiting this reduces the number of places to hide exploits. 4.4 - SET CUSTOM DATABASE TABLE PREFIX -------------------------------------------------------------------------------- If you're setting up a NEW WordPress install: During installation, change database table prefix from default 'wp_' to something random like 'wpr4nd0m_' This breaks automated SQL injection attacks that assume default table names. If your site is already live, changing this requires careful database migration. Only do this if you're comfortable with phpMyAdmin and SQL queries. 4.5 - ENABLE AUTOMATIC BACKGROUND UPDATES -------------------------------------------------------------------------------- /** Enable automatic core updates (security releases only) */ define('WP_AUTO_UPDATE_CORE', 'minor'); This ensures WordPress security patches are applied automatically without waiting for manual intervention. 4.6 - FORCE SSL FOR ADMIN AND LOGINS -------------------------------------------------------------------------------- /** Force SSL for admin dashboard and logins */ define('FORCE_SSL_ADMIN', true); This ensures login credentials are never transmitted in plaintext. Only add this if you have a valid SSL certificate installed. 4.7 - CHANGE SECURITY KEYS AND SALTS -------------------------------------------------------------------------------- WordPress uses 8 security keys/salts for cookie encryption. Generate new random keys: https://api.wordpress.org/secret-key/1.1/salt/ Replace the existing define() lines in wp-config.php with the new ones. Do this: - Immediately after a suspected compromise - Every 6-12 months as a precaution - If you inherited a site and don't trust the previous developer Changing keys logs out all users (including attackers). ================================================================================ SECTION 5: FILE PERMISSIONS (CHMOD) ================================================================================ Incorrect file permissions are a common security hole. Connect via SSH or use cPanel File Manager. 5.1 - RECOMMENDED PERMISSIONS -------------------------------------------------------------------------------- Set these permissions on your WordPress installation: # WordPress root directory chmod 755 /home/USERNAME/public_html # All directories find /home/USERNAME/public_html -type d -exec chmod 755 {} \; # All files find /home/USERNAME/public_html -type f -exec chmod 644 {} \; # wp-config.php (most critical file) chmod 440 /home/USERNAME/public_html/wp-config.php # .htaccess chmod 644 /home/USERNAME/public_html/.htaccess # wp-content (may need 755 if plugins write files) chmod 755 /home/USERNAME/public_html/wp-content # uploads directory (needs write access) chmod 755 /home/USERNAME/public_html/wp-content/uploads 5.2 - WHY THESE PERMISSIONS MATTER -------------------------------------------------------------------------------- 644 on files = Owner can read/write, everyone else read-only 755 on directories = Owner can read/write/execute, everyone else read/execute 440 on wp-config.php = Owner/group read-only, no write, no execute Never use 777 (world-writable) on anything. This allows any user on shared hosting to modify your files. 5.3 - OWNERSHIP (IF YOU HAVE ROOT/SUDO) -------------------------------------------------------------------------------- If you manage your own VPS: # Change owner to your user, group to Apache/nginx chown -R USERNAME:nobody /home/USERNAME/public_html Or if Apache runs as a different user: chown -R USERNAME:apache /home/USERNAME/public_html Check Apache user with: ps aux | grep httpd | head -1 ================================================================================ SECTION 6: DATABASE HARDENING ================================================================================ Login to phpMyAdmin via cPanel or WHM. 6.1 - REMOVE UNUSED ADMIN ACCOUNTS -------------------------------------------------------------------------------- Open database > wp_users table (or your prefix_users) Look for usernames like: - admin - administrator - test - demo - Any user you don't recognize Delete them if they're not yours. 6.2 - CHANGE ADMIN USERNAME FROM "admin" -------------------------------------------------------------------------------- If your admin username is "admin", change it. Method 1: Via phpMyAdmin - Open wp_users table - Find user with ID 1 - Change user_login from 'admin' to something unique - Change user_nicename to match Method 2: Create new admin, delete old - Login to WordPress dashboard - Users > Add New - Create new admin user with strong random username - Logout, login as new admin - Delete old 'admin' user - Assign all posts to new admin Never use predictable usernames like: - admin - administrator - your domain name - your first name 6.3 - ENFORCE STRONG PASSWORDS IN DATABASE -------------------------------------------------------------------------------- This is easier with a plugin (see Section 7), but you can verify password strength by checking user_pass hashes in wp_users table. WordPress uses bcrypt hashing. All user_pass values should start with '$P$' or '$2a$' (newer installs). If you see plain text passwords, your site is severely compromised. 6.4 - REMOVE DEBUG/BACKUP TABLES -------------------------------------------------------------------------------- Attackers create backup tables to restore malware after you clean it. Look for suspicious tables: - wp_users_backup - wp_old - Anything with random characters If you didn't create it, drop the table. ================================================================================ SECTION 7: SECURITY PLUGINS (MINIMAL RECOMMENDED) ================================================================================ While this guide focuses on server-level hardening, a security plugin adds important monitoring layers. 7.1 - WORDFENCE (RECOMMENDED) -------------------------------------------------------------------------------- Install: Plugins > Add New > Search "Wordfence" Configure: - Firewall > Protection Level: Extended Protection - Firewall > Rate Limiting: Enable (limit to 4 requests per second) - Login Security > Enable 2FA: ON (for all admin roles) - Login Security > Block after failed logins: 5 attempts - Login Security > Immediately block invalid usernames: ON - Scan > Schedule: Daily at low-traffic time - Tools > Import/Export: Export settings (backup your config) Wordfence Premium features worth paying for: - Real-time IP blacklist updates - Country blocking at application level - Scheduled malware scanning 7.2 - ALTERNATIVE: SUCURI SECURITY (FREE) -------------------------------------------------------------------------------- Install: Plugins > Add New > Search "Sucuri Security" Configure: - Settings > Hardening: Apply all hardening options - Settings > Post-Hack: Enable all post-hack options - Settings > Alerts: Enable email alerts for all security events 7.3 - WPS LIMIT LOGIN (LIGHTWEIGHT) -------------------------------------------------------------------------------- If you don't want a full security suite, use this for brute force protection: Install: Plugins > Add New > Search "WPS Limit Login" Configure: - Max login attempts: 3 - Lockout duration: 60 minutes 7.4 - WP 2FA (TWO-FACTOR AUTHENTICATION) -------------------------------------------------------------------------------- Install: Plugins > Add New > Search "WP 2FA" Configure: - Enforcement policy: Administrators (mandatory) - Grace period: 0 days (force immediately) - Backup codes: Enable Use with Google Authenticator, Authy, or any TOTP app. ================================================================================ SECTION 8: MONITORING AND MAINTENANCE ================================================================================ Security is not a one-time setup. You need ongoing monitoring. 8.1 - ENABLE WORDPRESS DEBUG LOG (HIDDEN) -------------------------------------------------------------------------------- Edit wp-config.php: /** Enable error logging but hide from public */ define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); @ini_set('display_errors', 0); Logs are written to: /wp-content/debug.log Check this file weekly for suspicious errors: - Unauthorized file access attempts - Plugin errors that might indicate exploit attempts - Database errors that suggest SQL injection 8.2 - MONITOR APACHE ACCESS LOGS -------------------------------------------------------------------------------- Via SSH: # Check last 100 requests tail -100 /home/USERNAME/access-logs/DOMAIN.com # Look for POST requests to wp-login.php (brute force) grep "POST /wp-login.php" /home/USERNAME/access-logs/DOMAIN.com | tail -50 # Look for xmlrpc.php requests (should be blocked) grep "xmlrpc.php" /home/USERNAME/access-logs/DOMAIN.com # Look for user enumeration attempts grep "wp-json/wp/v2/users" /home/USERNAME/access-logs/DOMAIN.com grep "?author=" /home/USERNAME/access-logs/DOMAIN.com If you see hundreds of requests from the same IP, block it in CSF or cPanel. 8.3 - CHECK FILE INTEGRITY WEEKLY -------------------------------------------------------------------------------- Via SSH: # List recently modified files (last 7 days) find /home/USERNAME/public_html -type f -mtime -7 -ls If you see modified core WordPress files you didn't touch, investigate immediately. Compare against a clean WordPress installation. 8.4 - REVIEW USER ACCOUNTS MONTHLY -------------------------------------------------------------------------------- Dashboard > Users Look for: - Users you don't recognize - Users with Administrator role who shouldn't have it - Inactive users with old passwords Delete or downgrade unnecessary admin accounts. 8.5 - UPDATE EVERYTHING REGULARLY -------------------------------------------------------------------------------- Every week: - Check for WordPress core updates (Dashboard > Updates) - Check for plugin updates - Check for theme updates Do not delay security updates. Attackers scan for outdated versions within hours of CVE disclosure. If you enabled DISALLOW_FILE_MODS in wp-config.php, update via WP-CLI: wp core update wp plugin update --all wp theme update --all ================================================================================ SECTION 9: RESPONSE CHECKLIST (IF YOU DETECT COMPROMISE) ================================================================================ If you suspect your site is compromised: 9.1 - IMMEDIATE ACTIONS -------------------------------------------------------------------------------- 1. Change all passwords: - WordPress admin users - cPanel account - FTP/SSH accounts - Database user password 2. Rotate WordPress security keys (Section 4.7) 3. Check wp_users table for rogue admin accounts 4. Scan all files for malware: - Use Wordfence scan - Compare core files against clean WordPress install - Check wp-content/uploads for PHP files (should be images only) 5. Review .htaccess for injected redirects (usually at top or bottom) 6. Check wp-config.php for injected code (usually before or after closing PHP tag) 9.2 - COMMON MALWARE LOCATIONS -------------------------------------------------------------------------------- Attackers hide malware in: /wp-content/uploads/*.php (shouldn't exist) /wp-includes/*.php (compare against clean install) /wp-admin/*.php (compare against clean install) /wp-content/themes/ACTIVE_THEME/*.php (check all theme files) /wp-content/plugins/*/backdoor.php (or similar) wp-config.php (before Export 2. Download wp-content/uploads directory (your media) 3. Delete ALL WordPress files via cPanel File Manager 4. Download fresh WordPress from wordpress.org 5. Upload and install fresh WordPress 6. Restore database 7. Restore uploads directory 8. Reinstall themes/plugins from official sources (DO NOT restore old plugin files) 9. Re-apply all hardening rules from this checklist ================================================================================ SECTION 10: ADVANCED HARDENING (OPTIONAL) ================================================================================ For high-security environments or sites under active attack. 10.1 - MOVE wp-config.php UP ONE DIRECTORY -------------------------------------------------------------------------------- WordPress allows wp-config.php to live one directory above web root. Move from: /home/USERNAME/public_html/wp-config.php To: /home/USERNAME/wp-config.php WordPress will automatically find it. This puts your database credentials outside the web-accessible directory. Update file permissions: chmod 440 /home/USERNAME/wp-config.php 10.2 - CHANGE wp-content AND wp-plugin DIRECTORY NAMES -------------------------------------------------------------------------------- Edit wp-config.php: /** Change wp-content directory name */ define('WP_CONTENT_DIR', '/home/USERNAME/public_html/assets'); define('WP_CONTENT_URL', 'https://yourdomain.com/assets'); /** Change plugins directory name */ define('WP_PLUGIN_DIR', '/home/USERNAME/public_html/assets/modules'); define('WP_PLUGIN_URL', 'https://yourdomain.com/assets/modules'); Then rename directories: mv wp-content assets mv assets/plugins assets/modules This breaks automated scanners looking for default paths. WARNING: Some plugins assume default paths and may break. 10.3 - USE A WEB APPLICATION FIREWALL (WAF) -------------------------------------------------------------------------------- Beyond server-level protection, use a cloud WAF: Option 1: Cloudflare (free tier available) - Add your domain to Cloudflare - Change nameservers to Cloudflare's - Enable: SSL/TLS > Full (strict) - Enable: Security > Bot Fight Mode - Enable: Firewall Rules > Block known bots - Enable: Rate Limiting > 100 requests per minute per IP Option 2: Sucuri Firewall (paid) - Cloud-based WAF - DDoS protection - Virtual patching for zero-day exploits Option 3: Wordfence Falcon (paid) - Server-level caching + WAF - Requires root access 10.4 - IMPLEMENT FAIL2BAN FOR SSH AND WP-LOGIN -------------------------------------------------------------------------------- If you have root/sudo access: Install fail2ban: yum install fail2ban Create WordPress jail: nano /etc/fail2ban/jail.local Add: [wordpress-hard] enabled = true filter = wordpress-hard logpath = /home/*/access-logs/*.com port = http,https bantime = 86400 maxretry = 3 Create filter: nano /etc/fail2ban/filter.d/wordpress-hard.conf Add: [Definition] failregex = ^ .* "POST /wp-login.php ^ .* "POST /xmlrpc.php ignoreregex = Restart fail2ban: systemctl restart fail2ban This automatically blocks IPs that attempt WordPress login brute force. 10.5 - IMPLEMENT SECURITY HEADERS -------------------------------------------------------------------------------- Add to .htaccess (inside block): # Prevent clickjacking Header always set X-Frame-Options "SAMEORIGIN" # Prevent MIME type sniffing Header always set X-Content-Type-Options "nosniff" # Enable XSS protection (older browsers) Header always set X-XSS-Protection "1; mode=block" # Referrer policy (don't leak URLs) Header always set Referrer-Policy "strict-origin-when-cross-origin" # Content Security Policy (strict) Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" # Permissions policy (disable unnecessary features) Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()" WARNING: CSP can break plugins that load external scripts. Test thoroughly. ================================================================================ SECTION 11: WHAT THIS ACCOMPLISHES ================================================================================ After implementing these measures: BLOCKED: - Automated username enumeration (REST API, author archives) - xmlrpc.php brute force and pingback DDoS - wp-login.php unlimited brute force attempts - Direct access to sensitive files (wp-config.php, .htaccess) - PHP execution in uploads directory - Known malicious user agents and exploit patterns - SQL injection via common query strings - File editing via WordPress dashboard (if configured) MONITORED: - Failed login attempts with IP blocking - File changes and malware signatures (with plugins) - Suspicious requests in Apache logs HARDENED: - Strong authentication with 2FA mandatory - Encrypted database credentials (security keys) - Limited PHP execution permissions - Forced HTTPS for sensitive operations - Automatic security updates DOES NOT PROTECT AGAINST: - Zero-day vulnerabilities (unpatched exploits) - Social engineering attacks (phishing for admin credentials) - Compromise via hosting provider (compromised cPanel account) - Supply chain attacks (malicious plugin from official repo) - Advanced persistent threats (targeted attack by skilled human) ================================================================================ WHAT YOU'VE ACTUALLY DONE ================================================================================ You have NOT made your site "unhackable." That does not exist. You HAVE made your site significantly more expensive to compromise. Every blocked enumeration attempt costs attackers time. Every rate-limited login attempt costs attackers resources. Every security layer adds operational cost to their attack. Most attackers are automated bots running scripts. They scan thousands of sites per hour looking for low-hanging fruit. If your site resists the first 3-5 automated probes, the bot moves on to easier targets. That's the goal. Not perfect security. Just being harder than average. Maintain these protections. Update regularly. Monitor logs weekly. Security is a process, not a product. ================================================================================ CHANGELOG ================================================================================ 2026-01-11: Initial checklist for CentOS 7.9 / cPanel 110 / Apache 2.4 Next review: 2026-07-11 (reassess based on new WordPress vulnerabilities) ================================================================================ CONTACT ================================================================================ Ben Huffman Dirt River Design ben@dirtriverdesign.com https://dirtriverdesign.com This file is shared as-is for educational purposes. Test thoroughly before deploying to production. Your server configuration may vary.