PHPwordpressadvanced
WordPress Limit Login Attempts
Add brute force protection by limiting failed login attempts
Faisal Yaqoob
November 5, 2025
#wordpress#security#login#brute-force#protection
Code
php
1 // Limit login attempts 2 function limit_login_attempts() { 3 // Get IP address 4 $ip = $_SERVER['REMOTE_ADDR']; 5
6 // Get transient 7 $attempts = get_transient('login_attempts_' . $ip); 8
9 if ($attempts === false) { 10 $attempts = 0; 11 } 12
13 return $attempts; 14 } 15
16 // Check if IP is blocked 17 function is_ip_blocked() { 18 $ip = $_SERVER['REMOTE_ADDR']; 19 $blocked = get_transient('login_blocked_' . $ip); 20
21 return $blocked !== false; 22 } 23
24 // Authenticate filter 25 function check_login_attempts($user, $username, $password) { 26 // Check if IP is blocked 27 if (is_ip_blocked()) { 28 $minutes = 30; // Block duration 29 return new WP_Error( 30 'too_many_attempts', 31 sprintf( 32 __('Too many failed login attempts. Please try again in %d minutes.'), 33 $minutes 34 ) 35 ); 36 } 37
38 return $user; 39 } 40 add_filter('authenticate', 'check_login_attempts', 30, 3); 41
42 // Track failed logins 43 function track_failed_login($username) { 44 $ip = $_SERVER['REMOTE_ADDR']; 45 $max_attempts = 5; // Maximum attempts allowed 46 $lockout_duration = 30 * 60; // 30 minutes in seconds 47
48 // Get current attempts 49 $attempts = limit_login_attempts(); 50 $attempts++; 51
52 // Set transient for 1 hour 53 set_transient('login_attempts_' . $ip, $attempts, 3600); 54
55 // Block IP if max attempts reached 56 if ($attempts >= $max_attempts) { 57 set_transient('login_blocked_' . $ip, true, $lockout_duration); 58
59 // Log the event 60 error_log(sprintf( 61 'IP %s blocked after %d failed login attempts for user: %s', 62 $ip, 63 $attempts, 64 $username 65 )); 66
67 // Optional: Send email notification 68 $admin_email = get_option('admin_email'); 69 $subject = 'Security Alert: Login Attempts Blocked'; 70 $message = sprintf( 71 "IP Address: %s\nUsername: %s\nAttempts: %d\nTime: %s", 72 $ip, 73 $username, 74 $attempts, 75 current_time('mysql') 76 ); 77
78 wp_mail($admin_email, $subject, $message); 79 } 80 } 81 add_action('wp_login_failed', 'track_failed_login'); 82
83 // Reset attempts on successful login 84 function reset_login_attempts($user_login, $user) { 85 $ip = $_SERVER['REMOTE_ADDR']; 86 delete_transient('login_attempts_' . $ip); 87 delete_transient('login_blocked_' . $ip); 88 } 89 add_action('wp_login', 'reset_login_attempts', 10, 2); 90
91 // Add custom error message 92 function custom_login_error_message($errors) { 93 if (is_ip_blocked()) { 94 $errors = new WP_Error(); 95 $errors->add( 96 'blocked', 97 __('<strong>ERROR</strong>: Too many failed login attempts. Your IP has been temporarily blocked.') 98 ); 99 } 100
101 return $errors; 102 } 103 add_filter('wp_login_errors', 'custom_login_error_message');
WordPress Limit Login Attempts
Protect your WordPress site from brute force attacks by limiting the number of failed login attempts from a single IP address.
// Limit login attempts
function limit_login_attempts() {
// Get IP address
$ip = $_SERVER['REMOTE_ADDR'];
// Get transient
$attempts = get_transient('login_attempts_' . $ip);
if ($attempts === false) {
$attempts = 0;
}
return $attempts;
}
// Check if IP is blocked
function is_ip_blocked() {
$ip = $_SERVER['REMOTE_ADDR'];
$blocked = get_transient('login_blocked_' . $ip);
return $blocked !== false;
}
// Authenticate filter
function check_login_attempts($user, $username, $password) {
// Check if IP is blocked
if (is_ip_blocked()) {
$minutes = 30; // Block duration
return new WP_Error(
'too_many_attempts',
sprintf(
__('Too many failed login attempts. Please try again in %d minutes.'),
$minutes
)
);
}
return $user;
}
add_filter('authenticate', 'check_login_attempts', 30, 3);
// Track failed logins
function track_failed_login($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$max_attempts = 5; // Maximum attempts allowed
$lockout_duration = 30 * 60; // 30 minutes in seconds
// Get current attempts
$attempts = limit_login_attempts();
$attempts++;
// Set transient for 1 hour
set_transient('login_attempts_' . $ip, $attempts, 3600);
// Block IP if max attempts reached
if ($attempts >= $max_attempts) {
set_transient('login_blocked_' . $ip, true, $lockout_duration);
// Log the event
error_log(sprintf(
'IP %s blocked after %d failed login attempts for user: %s',
$ip,
$attempts,
$username
));
// Optional: Send email notification
$admin_email = get_option('admin_email');
$subject = 'Security Alert: Login Attempts Blocked';
$message = sprintf(
"IP Address: %s\nUsername: %s\nAttempts: %d\nTime: %s",
$ip,
$username,
$attempts,
current_time('mysql')
);
wp_mail($admin_email, $subject, $message);
}
}
add_action('wp_login_failed', 'track_failed_login');
// Reset attempts on successful login
function reset_login_attempts($user_login, $user) {
$ip = $_SERVER['REMOTE_ADDR'];
delete_transient('login_attempts_' . $ip);
delete_transient('login_blocked_' . $ip);
}
add_action('wp_login', 'reset_login_attempts', 10, 2);
// Add custom error message
function custom_login_error_message($errors) {
if (is_ip_blocked()) {
$errors = new WP_Error();
$errors->add(
'blocked',
__('<strong>ERROR</strong>: Too many failed login attempts. Your IP has been temporarily blocked.')
);
}
return $errors;
}
add_filter('wp_login_errors', 'custom_login_error_message');
Admin Notification Function
// Get blocked IPs count
function get_blocked_ips_count() {
global $wpdb;
$query = "SELECT COUNT(*) FROM {$wpdb->options}
WHERE option_name LIKE '_transient_login_blocked_%'";
return $wpdb->get_var($query);
}
// Display admin notice
function login_attempts_admin_notice() {
$blocked_count = get_blocked_ips_count();
if ($blocked_count > 0) {
echo '<div class="notice notice-warning is-dismissible">';
echo '<p><strong>Security Notice:</strong> ' . $blocked_count . ' IP address(es) currently blocked due to failed login attempts.</p>';
echo '</div>';
}
}
add_action('admin_notices', 'login_attempts_admin_notice');
Advanced: Whitelist IPs
// Whitelist specific IPs
function is_ip_whitelisted() {
$ip = $_SERVER['REMOTE_ADDR'];
$whitelist = array(
'192.168.1.1',
'10.0.0.1',
// Add your IPs here
);
return in_array($ip, $whitelist);
}
// Modified authenticate check with whitelist
function check_login_attempts_with_whitelist($user, $username, $password) {
// Skip check for whitelisted IPs
if (is_ip_whitelisted()) {
return $user;
}
if (is_ip_blocked()) {
return new WP_Error(
'too_many_attempts',
__('Too many failed login attempts. Please try again later.')
);
}
return $user;
}
add_filter('authenticate', 'check_login_attempts_with_whitelist', 30, 3);
Configuration Options
// Customizable settings
define('LOGIN_MAX_ATTEMPTS', 5); // Maximum failed attempts
define('LOGIN_LOCKOUT_DURATION', 1800); // Lockout time in seconds (30 min)
define('LOGIN_ATTEMPTS_WINDOW', 3600); // Time window for attempts (1 hour)
Features
- Brute Force Protection: Limits failed login attempts
- IP Blocking: Temporarily blocks suspicious IPs
- Email Notifications: Alerts admins of security events
- Whitelist Support: Bypass protection for trusted IPs
- Customizable: Adjust attempt limits and durations
- Logging: Track failed attempts for security auditing
Related Snippets
Disable WordPress XML-RPC
Disable XML-RPC to improve security and prevent brute force attacks
PHPwordpressbeginner
phpPreview
// Method 1: Completely disable XML-RPC
add_filter('xmlrpc_enabled', '__return_false');
// Method 2: Block XML-RPC requests
...#wordpress#security#xmlrpc+1
11/9/2025
View
WordPress Custom Login Redirect by Role
Redirect users to different pages after login based on their user role
PHPwordpressintermediate
phpPreview
// Redirect users after login based on role
function custom_login_redirect($redirect_to, $request, $user) {
// Check if user is set and has roles
if (isset($user->roles) && is_array($user->roles)) {
...#wordpress#login#redirect+2
11/4/2025
View
Enable SVG Upload Support in WordPress
Safely allow SVG file uploads in WordPress media library
PHPwordpressintermediate
phpPreview
// Enable SVG uploads
function enable_svg_upload($mimes) {
$mimes['svg'] = 'image/svg+xml';
$mimes['svgz'] = 'image/svg+xml';
...#wordpress#svg#media+2
11/3/2025
View