Every WordPress site we host has a login rate limit, so after too many attempts from one address the login page is blocked for a while. It’s a standard defense against password guessing and it had been running quietly for months, until a customer wrote to us in July that they kept getting locked out of their own site. They weren’t typing wrong passwords, they were just logging out and back in while working on the site, and after a few rounds wp-admin refused to let them in for ten minutes.
What the limiter counted
The limiter increased its counter every time the login page loaded, and WordPress shows that page quite often in a normal session. It loads when you open the login form, again when you log out, because WordPress sends you back there, and again when your session expires and you’re asked to log in. None of those are failed logins, but each one counted, so a few ordinary logout and login cycles were enough to hit the limit.
The fix
The counter now only goes up on an actual failed login. WordPress has a hook for exactly that, wp_login_failed, which runs after a wrong username or password:
add_action('wp_login_failed', function () {
$key = 'login_fail_' . md5($_SERVER['REMOTE_ADDR']);
$attempts = (int) get_transient($key);
set_transient($key, $attempts + 1, 10 * MINUTE_IN_SECONDS);
});
Loading the login page now only checks the counter. After five failed logins from the same address, further attempts get a “429 Too Many Requests” response with a Retry-After header of ten minutes, while opening the form, logging out and expired sessions don’t count at all. We rolled the fix out to every site at the same time.
If you run your own login limiter, make it count failed logins rather than visits to wp-login.php.