Brute force attacks on WordPress login pages are relentless. Bots try thousands of username/password combinations until they gain access. These attacks consume server resources, slow down your site, and eventually succeed against weak passwords.
This guide provides multiple defensive layers to block brute force attacks, from limiting login attempts to IP whitelisting and CAPTCHA implementation.
Understanding Brute Force Attacks
How They Work
Attackers use automated scripts to try common passwords against wp-login.php:
admin / password
admin / 123456
admin / admin123
administrator / password
...thousands more combinations
Signs of an Attack
- Hundreds of failed login attempts in logs
- Increased server CPU/memory usage
- Slow admin dashboard
- Email flood of failed login notifications
- IP addresses from foreign countries
- Automated bot patterns (rapid-fire attempts)
Layer 1: Limit Login Attempts
Using Limit Login Attempts Reloaded
# Install via WP-CLI
wp plugin install limit-login-attempts-reloaded --activateRecommended Settings:
- Allowed attempts: 4
- Lockout duration: 20 minutes
- Reset after: 12 hours
- Long lockout: 24 hours after 4 lockouts
Manual Implementation:
// In functions.php or custom plugin
add_action('wp_login_failed', 'track_failed_login');
function track_failed_login($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$attempts = get_transient('failed_login_' . $ip) ?: 0;
$attempts++;
set_transient('failed_login_' . $ip, $attempts, 20 * MINUTE_IN_SECONDS);
if ($attempts >= 5) {
// Lock out this IP
set_transient('lockout_' . $ip, true, HOUR_IN_SECONDS);
wp_die('Too many failed login attempts. Please try again in 1 hour.');
}
}
// Check before allowing login
add_filter('authenticate', 'check_lockout', 30, 3);
function check_lockout($user, $username, $password) {
$ip = $_SERVER['REMOTE_ADDR'];
if (get_transient('lockout_' . $ip)) {
return new WP_Error('lockout', 'Account locked due to too many failed attempts.');
}
return $user;
}Layer 2: Add CAPTCHA Protection
Google reCAPTCHA v3 Integration
// Add to functions.php
// Enqueue reCAPTCHA script
add_action('login_enqueue_scripts', 'add_recaptcha_to_login');
function add_recaptcha_to_login() {
?>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script>
function onSubmit(token) {
document.getElementById("loginform").submit();
}
</script>
<?php
}
// Add reCAPTCHA to login form
add_action('login_form', 'display_recaptcha');
function display_recaptcha() {
?>
<div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY" data-callback="onSubmit"></div>
<?php
}
// Verify reCAPTCHA
add_filter('authenticate', 'verify_recaptcha', 30, 3);
function verify_recaptcha($user, $username, $password) {
if (empty($_POST['g-recaptcha-response'])) {
return new WP_Error('captcha_failed', 'Please complete the reCAPTCHA.');
}
$recaptcha = $_POST['g-recaptcha-response'];
$secret_key = 'YOUR_SECRET_KEY';
$response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', [
'body' => [
'secret' => $secret_key,
'response' => $recaptcha,
'remoteip' => $_SERVER['REMOTE_ADDR']
]
]);
$response_body = json_decode(wp_remote_retrieve_body($response));
if (!$response_body->success || $response_body->score < 0.5) {
return new WP_Error('captcha_failed', 'reCAPTCHA verification failed.');
}
return $user;
}Layer 3: Change Login URL
Using WPS Hide Login Plugin
wp plugin install wps-hide-login --activateSettings: Change /wp-login.php to /my-secure-login
Manual Method (via .htaccess):
# Redirect wp-login.php to custom URL
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/wp-login\.php
RewriteCond %{QUERY_STRING} !^action=logout
RewriteCond %{QUERY_STRING} !^action=rp
RewriteCond %{QUERY_STRING} !^action=register
RewriteRule ^(.*)$ /custom-login-url? [R=301,L]Caution: Remember your custom URL. Losing it locks you out.
Layer 4: IP Whitelisting
Restrict wp-admin by IP
# In wp-admin/.htaccess
<Files admin-ajax.php>
Order allow,deny
Allow from all
Satisfy any
</Files>
# Block all other wp-admin access except your IP
Order deny,allow
Deny from all
Allow from 123.456.789.0
Allow from 987.654.321.0For Nginx:
location ~* /wp-admin/ {
allow 123.456.789.0;
deny all;
}
Dynamic IP Solution (for VPN/mobile users):
// In functions.php - Email-based IP whitelisting
add_action('wp_login_failed', 'email_whitelist_request');
function email_whitelist_request($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$whitelist = get_option('ip_whitelist', []);
if (!in_array($ip, $whitelist)) {
// Send email to admin with "approve" link
$approve_url = admin_url('admin.php?action=approve_ip&ip=' . $ip);
wp_mail(
get_option('admin_email'),
'New IP Login Attempt',
"Approve IP $ip: $approve_url"
);
}
}Layer 5: WordPress Firewall
Wordfence Configuration
wp plugin install wordfence --activateEssential Settings:
-
Enable Extended Protection (premium feature)
-
Brute Force Protection:
- Enable login page CAPTCHA
- Immediately block invalid usernames
- Lock out after 5 failed logins
-
Advanced Blocking:
- Block attackers from specific countries
- Throttle login attempts
- Block known malicious IPs
Rate Limiting Example:
// Wordfence alternative - manual rate limiting
add_action('login_form', 'add_login_delay');
function add_login_delay() {
$ip = $_SERVER['REMOTE_ADDR'];
$last_attempt = get_transient('last_login_attempt_' . $ip);
if ($last_attempt && (time() - $last_attempt) < 3) {
sleep(3); // Force 3-second delay between attempts
}
set_transient('last_login_attempt_' . $ip, time(), MINUTE_IN_SECONDS);
}Layer 6: Cloudflare Protection
Enable Cloudflare Firewall Rules
- Sign up for Cloudflare (free plan works)
- Add your domain
- Update nameservers
- Enable “Under Attack Mode” during active brute force
Firewall Rule:
(http.request.uri.path contains "/wp-login.php") and
(not ip.geoip.country in {"US" "CA" "GB"})
This blocks non-US/CA/GB traffic to login page.
Layer 7: Two-Factor Authentication
Combine 2FA with login limits for ultimate protection:
wp plugin install two-factor --activateEven if attackers guess passwords, they can’t login without the second factor.
Layer 8: Disable XML-RPC
XML-RPC can be used for brute force attacks:
# In .htaccess
<Files xmlrpc.php>
Order deny,allow
Deny from all
</Files>Or via plugin code:
// Completely disable XML-RPC
add_filter('xmlrpc_enabled', '__return_false');
// Or disable only certain methods
add_filter('xmlrpc_methods', 'disable_xmlrpc_methods');
function disable_xmlrpc_methods($methods) {
unset($methods['wp.getUsersBlogs']);
unset($methods['system.multicall']);
unset($methods['system.listMethods']);
return $methods;
}Layer 9: Server-Level Protection (Fail2Ban)
Configure Fail2Ban for WordPress
# Install Fail2Ban (Ubuntu/Debian)
sudo apt-get install fail2ban
# Create WordPress filter
sudo nano /etc/fail2ban/filter.d/wordpress.confFilter Content:
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php
^<HOST> .* "POST /xmlrpc.php
ignoreregex =Jail Configuration:
# In /etc/fail2ban/jail.local
[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/apache2/access.log
maxretry = 5
bantime = 3600
findtime = 600Restart Fail2Ban:
sudo systemctl restart fail2banMonitoring and Alerts
Email Notifications
// Email admin on failed logins
add_action('wp_login_failed', 'notify_failed_login');
function notify_failed_login($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$time = current_time('mysql');
wp_mail(
get_option('admin_email'),
'Failed Login Attempt',
"Failed login for username: $username\nIP: $ip\nTime: $time"
);
}Log Analysis
# View recent failed logins (Apache)
grep "wp-login.php" /var/log/apache2/access.log | grep "POST" | tail -50
# Count attempts by IP
awk '{print $1}' /var/log/apache2/access.log | grep -v "^$" | sort | uniq -c | sort -rn | head -20Testing Your Protection
Safe Simulation
- Use incognito browser
- Attempt 5-6 failed logins
- Verify lockout occurs
- Check email notifications
- Confirm CAPTCHA appears (if configured)
- Test IP whitelist (from different IP)
Automated Test:
# Test with curl (safely)
for i in {1..6}; do
curl -X POST https://yoursite.com/wp-login.php \
-d "log=testuser&pwd=wrongpassword"
sleep 2
doneBest Practices Summary
✅ Do:
- Limit login attempts (4-5 max)
- Add CAPTCHA protection
- Use strong, unique passwords
- Enable 2FA for all admins
- Monitor failed login logs
- Update security plugins regularly
❌ Don’t:
- Use “admin” as username
- Allow unlimited login attempts
- Ignore failed login notifications
- Use same password across accounts
- Disable security features for convenience
Complete Protection Stack
Recommended combination:
- Strong passwords (20+ characters)
- Limit Login Attempts Reloaded (free)
- Google reCAPTCHA v3 (free)
- Wordfence or Sucuri (free/premium)
- Two-Factor Authentication (free)
- Cloudflare (free tier sufficient)
- Fail2Ban (server-level, free)
This multi-layered approach stops 99.9% of brute force attacks while maintaining usability for legitimate users.
Brute force attacks are preventable. By implementing these protective layers, you transform WordPress login from a vulnerable entry point into a hardened fortress. Start with basic login limits and CAPTCHA, then add additional layers based on your threat level and resources.
External Links
Call to Action
Secure your site with bulletproof backups! Backup Copilot Pro offers automated security audits, malware scanning before backups, and instant recovery—try it free!

