wordpress security brute force login

How to Limit Login Attempts on WordPress(No Plugins)

WordPress, despite being the most popular Content Management System (CMS) on the web in 2025, responsible for approximately 40% of all websites, still lacks a default setting to limit login attempts.

This fundamental security feature is crucial for preventing brute force attacks, where malicious actors repeatedly try billions of username and password combinations until they gain unauthorized access to an account. In conjunction with two-factor authentication, which every administrator should have enabled, limiting login attempts is one of the most critical measures to ensure a website’s security.

In this tutorial, I will demonstrate a simple code snippet that you can easily incorporate into your WordPress site, and it can likely be adapted for other CMS platforms as well, to enhance your security posture. By implementing this code, you will add an essential layer of protection against brute force attacks, making your website more resilient to such threats.

The code

function limit_login_attempts_idc() {
    $attempt_login = get_transient('attempt_login');

    if (false === $attempt_login) {
        $attempt_login = array();
    }

    $ip = $_SERVER['REMOTE_ADDR'];
    $attempt_login[$ip] = isset($attempt_login[$ip]) ? $attempt_login[$ip] : 0;
    $attempt_login[$ip]++;

    set_transient('attempt_login', $attempt_login, 60);

    if ($attempt_login[$ip] > 2) {
        wp_die("You have exceeded the maximum number of login attempts. Please try again in 60 seconds.");
    }

}
add_action( 'wp_login_failed' ,'limit_login_attempts_idc');

The code could not be any simpler:

  • A transient to keep track of login attempts per IP
  • $_SERVER[‘REMOTE_ADDR’] to get the visitor’s IP
  • A simple check to see if the user attempted login more than twice in 60 seconds
  • A “block”(wp_die, which will just redirect the user to an empty page with a message) in case the user tried too many times and failed

The transient gets automatically deleted by wordpress itself once its no longer useful, and you no longer need to worry about brute force attacks, because trying to do so being blocked for 60 seconds every 2 fails means waiting a millions of years to get the right user/password.

For other platforms

If you wish for a more generic code, that should work on any platform that runs on php, you could try:

<?php
session_start();

function limit_login_attempts_idc() {
    // Use session to store login attempts
    if (!isset($_SESSION['attempt_login'])) {
        $_SESSION['attempt_login'] = [];
    }

    $ip = $_SERVER['REMOTE_ADDR'];
    if (!isset($_SESSION['attempt_login'][$ip])) {
        $_SESSION['attempt_login'][$ip] = 0;
    }

    $_SESSION['attempt_login'][$ip]++;

    // Set a timeout for the session data (60 seconds)
    $_SESSION['attempt_login_expiry'] = time() + 60;

    if ($_SESSION['attempt_login'][$ip] > 2) {
        die("You have exceeded the maximum number of login attempts. Please try again in 60 seconds.");
    }

    // Clean up old attempts
    if (time() > $_SESSION['attempt_login_expiry']) {
        unset($_SESSION['attempt_login'][$ip]);
    }
}

// Call this function upon a failed login attempt
// Note: You need to integrate this function call into your authentication system's failure hook
// This is a generic example and may need adjustments based on your specific system
function check_failed_login() {
    limit_login_attempts_idc();
}

// Example integration (replace with actual login failure hook in your system)
if (isset($_POST['login']) && !validate_login($_POST['username'], $_POST['password'])) {
    check_failed_login();
    // Rest of your failed login handling code
}

function validate_login($username, $password) {
    // Stub function for login validation
    // Replace with your actual user authentication logic
    return false;
}
?>

This will check any post requests to a form named “login” and try to validate the fields “username” and “password”. If they are wrong more than twice, the IP is blocked from trying again for 60 seconds. Same as the previous one.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *