WordPress powers a large portion of websites on the internet, from personal blogs to large e-commerce platforms. This ubiquity makes it an attractive target for attackers. Automated bots constantly scan for vulnerable WordPress installations, outdated plugins, and weak credentials. A compromised WordPress site can serve malware, steal customer data, send spam, or become part of a botnet. This guide covers essential WordPress security hardening measures, from core configuration to plugin assessment and admin protection, and explains how agentic penetration testing can identify vulnerabilities before attackers exploit them.
Understanding WordPress Security Risks
WordPress security challenges stem from several factors:
- Plugin ecosystem: The large plugin ecosystem varies widely in code quality and security practices
- Shared hosting environments: Many WordPress sites run on shared hosts with limited security controls
- Default configurations: Out-of-the-box WordPress settings prioritize ease of use over security
- Update lag: Many sites run outdated WordPress core, themes, or plugins
- Weak credentials: Default usernames and weak passwords remain common
Common attack vectors include:
- Brute force attacks against wp-admin and XML-RPC
- SQL injection through vulnerable plugins
- Cross-site scripting (XSS) in themes and plugins
- File inclusion vulnerabilities allowing remote code execution
- Privilege escalation from subscriber to admin
WordPress Core Security Configuration
Start with securing the WordPress core installation itself.
wp-config.php Hardening
The wp-config.php file controls critical WordPress settings. Move it above the web root if possible, and add these security configurations:
<?php
// Disable file editing in admin panel
define('DISALLOW_FILE_EDIT', true);
// Limit post revisions to reduce database bloat and attack surface
define('WP_POST_REVISIONS', 5);
// Force SSL for admin
define('FORCE_SSL_ADMIN', true);
// Disable XML-RPC if not needed (reduces brute force attack surface)
// Handle in .htaccess or use a plugin for more granular control
// Authentication keys and salts (generate unique ones)
// https://api.wordpress.org/secret-key/1.1/salt/
define('AUTH_KEY', 'your-unique-phrase-here');
define('SECURE_AUTH_KEY', 'your-unique-phrase-here');
define('LOGGED_IN_KEY', 'your-unique-phrase-here');
define('NONCE_KEY', 'your-unique-phrase-here');
define('AUTH_SALT', 'your-unique-phrase-here');
define('SECURE_AUTH_SALT', 'your-unique-phrase-here');
define('LOGGED_IN_SALT', 'your-unique-phrase-here');
define('NONCE_SALT', 'your-unique-phrase-here');
// Database table prefix - change from default 'wp_'
$table_prefix = 'wp_a1b2c3_';
// Disable debug mode in production
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);
// Automatic updates for security releases
define('WP_AUTO_UPDATE_CORE', 'minor');.htaccess Security Rules
Add these directives to your .htaccess file:
# Protect wp-config.php
<files wp-config.php>
order allow,deny
deny from all
</files>
# Protect .htaccess
<files .htaccess>
order allow,deny
deny from all
</files>
# Disable directory browsing
Options -Indexes
# Block access to sensitive files
<FilesMatch "^.*(error_log|wp-config\.php|php\.ini|\.[hH][tT][aApP].*)$">
Order deny,allow
Deny from all
</FilesMatch>
# Protect wp-includes
<IfModule mod_rewrite.c>
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]
</IfModule>
# Disable XML-RPC
<Files xmlrpc.php>
order deny,allow
deny from all
</Files>
# Limit login attempts by blocking common bad actors (requires mod_rewrite)
# Consider using a plugin for more sophisticated rate limitingDatabase Security
Secure your WordPress database:
-- Create a dedicated user with minimal privileges
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'strong_password_here';
-- Grant only necessary permissions
GRANT SELECT, INSERT, UPDATE, DELETE ON wordpress_db.* TO 'wp_user'@'localhost';
-- Don't grant FILE, PROCESS, or administrative privileges
-- Avoid granting ALTER, DROP, CREATE unless during updates
FLUSH PRIVILEGES;Plugin Security Assessment
Plugins are the most common source of WordPress vulnerabilities. Implement a rigorous assessment process.
Before Installing Plugins
Evaluate plugins before installation:
- Check last update date: Plugins not updated in 2+ years may have unpatched vulnerabilities
- Review active installations: Higher numbers suggest community vetting
- Read recent reviews: Look for security-related complaints
- Check the changelog: Active security patching is a good sign
- Search vulnerability databases: Check WPScan, CVE databases, and security blogs
Auditing Installed Plugins
Regularly audit your plugin inventory:
# List all plugins with their status
wp plugin list --format=table
# Check for available updates
wp plugin update --all --dry-run
# Identify inactive plugins (remove these)
wp plugin list --status=inactive
# Check plugin checksums against WordPress.org
wp plugin verify-checksums --allPlugin Security Checklist
For each plugin, verify:
- Plugin is from a reputable source (WordPress.org, known vendor)
- Plugin is actively maintained (updated within last 6 months)
- Plugin has no known unpatched vulnerabilities
- Plugin uses nonces for form submissions
- Plugin properly sanitizes and escapes output
- Plugin uses prepared statements for database queries
- Plugin follows WordPress capability checks
Removing Unnecessary Plugins
Every installed plugin expands your attack surface:
# Deactivate and delete unused plugins
wp plugin deactivate plugin-name
wp plugin delete plugin-name
# List plugins and their sizes
wp plugin list --fields=name,status --format=csv | while read line; do
name=$(echo $line | cut -d',' -f1)
du -sh wp-content/plugins/$name 2>/dev/null
doneAdmin Hardening
The WordPress admin area is a primary target. Implement layered protection.
Change Default Username
Never use "admin" as a username:
# Create a new admin user
wp user create newadmin newadmin@example.com --role=administrator --user_pass=strong_password
# Reassign posts from old admin
wp post list --author=1 --format=ids | xargs -I {} wp post update {} --post_author=NEW_USER_ID
# Delete the old admin user
wp user delete admin --reassign=NEW_USER_IDImplement Two-Factor Authentication
Add 2FA for all admin and editor accounts. Use plugins like:
- Wordfence (includes 2FA)
- Two-Factor (official WordPress plugin)
- Google Authenticator
Limit Login Attempts
Protect against brute force attacks:
// In your theme's functions.php or a custom plugin
function custom_limit_login_attempts($user, $username, $password) {
$ip = $_SERVER['REMOTE_ADDR'];
$lockout_duration = 15 * MINUTE_IN_SECONDS;
$max_attempts = 5;
$attempts = get_transient('login_attempts_' . $ip);
if ($attempts >= $max_attempts) {
return new WP_Error(
'too_many_attempts',
'Too many failed login attempts. Please try again later.'
);
}
return $user;
}
add_filter('authenticate', 'custom_limit_login_attempts', 30, 3);
function track_failed_login($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$attempts = get_transient('login_attempts_' . $ip) ?: 0;
set_transient('login_attempts_' . $ip, $attempts + 1, 15 * MINUTE_IN_SECONDS);
}
add_action('wp_login_failed', 'track_failed_login');Change Login URL
Move the login page from the default /wp-admin:
// Using a plugin like WPS Hide Login is recommended
// Or implement manually with careful testing
function custom_login_url($login_url, $redirect, $force_reauth) {
return home_url('/secure-login/');
}
add_filter('login_url', 'custom_login_url', 10, 3);Restrict Admin Access by IP
If your admin users have static IPs:
# In .htaccess within wp-admin directory
<Files *.php>
order deny,allow
deny from all
allow from YOUR.IP.ADDRESS.HERE
allow from ANOTHER.ALLOWED.IP
</Files>Disable User Enumeration
Prevent attackers from discovering valid usernames:
// Block author archives that reveal usernames
function disable_author_archives() {
if (is_author()) {
global $wp_query;
$wp_query->set_404();
status_header(404);
nocache_headers();
}
}
add_action('template_redirect', 'disable_author_archives');
// Block REST API user enumeration for non-authenticated users
function disable_rest_user_enumeration($response, $user, $request) {
if (!current_user_can('list_users')) {
return new WP_Error(
'rest_forbidden',
'You do not have permission to access this resource.',
array('status' => 403)
);
}
return $response;
}
add_filter('rest_prepare_user', 'disable_rest_user_enumeration', 10, 3);Security Headers
Add security headers via .htaccess or a plugin:
<IfModule mod_headers.c>
# Prevent clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Prevent MIME type sniffing
Header always set X-Content-Type-Options "nosniff"
# Enable XSS filter
Header always set X-XSS-Protection "1; mode=block"
# Referrer policy
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Content Security Policy (customize based on your needs)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
# Strict Transport Security (only if using HTTPS)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>File Permissions
Set correct file permissions to prevent unauthorized modifications:
# Directories should be 755 or 750
find /path/to/wordpress/ -type d -exec chmod 755 {} \;
# Files should be 644 or 640
find /path/to/wordpress/ -type f -exec chmod 644 {} \;
# wp-config.php should be more restrictive
chmod 600 wp-config.php
# wp-content/uploads needs write access but no PHP execution
# Handle PHP execution blocking in .htaccessCreate .htaccess in wp-content/uploads:
# Prevent PHP execution in uploads directory
<Files *.php>
deny from all
</Files>Monitoring and Logging
Enable logging to detect attacks:
// Log failed login attempts
function log_failed_login($username) {
$log_file = WP_CONTENT_DIR . '/security-log.txt';
$timestamp = current_time('mysql');
$ip = $_SERVER['REMOTE_ADDR'];
$user_agent = $_SERVER['HTTP_USER_AGENT'];
$log_entry = sprintf(
"[%s] Failed login for user '%s' from IP %s - UA: %s\n",
$timestamp,
sanitize_user($username),
$ip,
substr($user_agent, 0, 100)
);
file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);
}
add_action('wp_login_failed', 'log_failed_login');Why Traditional Pentesting Falls Short
WordPress security testing presents unique challenges. The platform's flexibility means every installation is different—different themes, different plugins, different configurations. Manual penetration testers must understand WordPress internals, identify which of thousands of possible plugins are installed, and test each component for vulnerabilities.
Traditional pentests are also point-in-time assessments. WordPress sites change frequently: plugins update, content changes, new plugins get installed. A vulnerability could be introduced a week after the pentest, leaving the site exposed until the next assessment.
Additionally, WordPress vulnerabilities are often discovered rapidly. New plugin vulnerabilities are disclosed weekly, and attackers quickly build exploits. Annual or quarterly pentests can't keep pace with this threat landscape.
How Agentic Testing Secures WordPress Sites
RedVeil's AI-powered penetration testing is designed to handle the complexity of WordPress security. RedVeil's agents autonomously discover your WordPress installation's components—core version, themes, plugins—and test each for known vulnerabilities and misconfigurations.
RedVeil doesn't just check for outdated versions. It tests authentication flows, probes for SQL injection and XSS in installed plugins, and identifies configuration weaknesses that could be exploited. When RedVeil finds a vulnerability, it validates the finding with proof-of-concept evidence, eliminating false positives.
With on-demand testing, you can assess your WordPress site's security after updates, plugin installations, or configuration changes. Each finding includes clear remediation guidance specific to WordPress, helping you quickly close security gaps.
Conclusion
WordPress security requires attention to core configuration, careful plugin management, and robust admin protection. The platform's popularity makes it a constant target, and default configurations leave many sites vulnerable.
Traditional security testing struggles to keep pace with WordPress's dynamic nature and the constant stream of plugin vulnerabilities. On-demand, agentic penetration testing from RedVeil provides the autonomous, intelligent assessment WordPress sites need to stay secure.
Start securing your WordPress site with RedVeil at https://app.redveil.ai/ and protect your site from the threats targeting WordPress installations daily.