How to use Fail2Ban to protect WordPress

WordPress Auth

1. Create a Must-Use plugin

Fail2Ban depends on log entries but WordPress does not log authentication requests on its own. So we will start with creating a small WordPress plugin that logs failed authorization attempts via wp-login.php and xmlrpc.php. This plugin will write to the server auth.log which will be monitored by fail2ban. It can run on both regular and multi-site WordPress installations.

First, create a file fail2ban.php in your WordPress wp-content/mu-plugins/ directory.

The directory may not exist yet. In the below example, WordPress is installed in /var/www/mysite.com/htdocs/

sudo mkdir /var/www/mysite.com/htdocs/wp-content/mu-plugins

Create the file:

sudo nano /var/www/mysite.com/htdocs/wp-content/mu-plugins/fail2ban.php

Then, copy the following content and use your mouse right-click or Ctrl+Shift+V (yes, add shift!) to paste into that file:

<?php
/**
 * Plugin name: Log failed authentication attempts for Fail2Ban 
 * Plugin URI: https://status301.net/how-to-use-fail2ban-to-protect-wordpress/
 * Description: Add failed authentication attempts to the server auth.log
 * Version: 1.0
 * Author: RavanH
 * Author URI: https://status301.net/
 */

 || die();

add_action( 'wp_login_failed', function( $username ) {
    openlog( 'wordpress', LOG_PID | LOG_NDELAY, LOG_AUTHPRIV );

    syslog( LOG_WARNING, "Failed authentication attempt with username {$username} from {$_SERVER['REMOTE_ADDR']} ({$_SERVER['HTTP_USER_AGENT']})");

    closelog();
} );

Finally, hit Ctrl+X and confirm with Y to save the file and close the editor.

If you have multiple WordPress installations, you’ll have to repeat this process for each one.

2. Configure Fail2Ban

A. Create the WordPress Auth filter

There is no dedicated filter for WordPress among the default fail2ban filters so we will have to create a new one. The filter defines what fail2ban should look for in the log file. The “failregex” value matches the log entries created by our must-use plugin.

sudo nano /etc/fail2ban/filter.d/wordpress-auth.conf

Copy the following content and use your mouse right-click or Ctrl+Shift+V to paste into that file:

# Fail2Ban filter for WordPress Authentication failures
#

[INCLUDES]

before = common.conf

[Definition]

_daemon = wordpress

failregex = ^%(__prefix_line)sFailed authentication attempt with username <F-USER>.*</F-USER> from <ADDR> .*$

ignoreregex =

# DEV Notes:
# Requires a must-use plugin: https://status301.net/how-to-use-fail2ban-to-protect-your-wordpress-site/
#
# Author: RavanH

B. Create the WordPress Auth jail

With the filter rule in place, we can now set the ban or jail rules.

sudo nano /etc/fail2ban/jail.d/wordpress-auth.conf

Copy the following content and use your mouse right-click or Ctrl+Shift+V to paste into that file:

[wordpress-auth]
enabled = true
port = http,https
logpath = %(syslog_authpriv)s
backend = auto

Do a full restart of the fail2ban service:

sudo service fail2ban restart
Adjust the default jail rules (optional)

By default, fail2ban will use the following settings.

maxretry = 5findtime = 10m
bantime  = 10m

If a host has generated maxretry failures during the last findtime then it will be banned for a duration of bantime (in seconds, or minutes if “m” is added)

These determine the number of failed attempts, during what time frame and the time an IP address will be banned. To make your jail either more — or less strict, add these to the wordpress-auth.conf file and modify their values to fit your needs.

For example, increase the time an offending host is banned to 30 minutes with bantime = 30m

Or make it both less restrictive and activate sooner at the same time with findtime = 60 (in seconds, equivalent of 1m) and maxretry = 3 allowing 3 login attempts each minute.

After a modification of the jail parameters, reload the jail with:

sudo fail2ban-client reload wordpress-auth
Verify jail status

We can use the command fail2ban-client status via ssh to get details.

First, check if the new wordpress-auth jail is loaded. It should appear in the “Jail list”:

$ sudo fail2ban-client status
Status
|- Number of jail:	2
`- Jail list:	sshd, wordpress-auth

Then verify the jail details:

$ sudo fail2ban-client status wordpress-auth
Status for the jail: wordpress-auth
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	0
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	0
   |- Total banned:	0
   `- Banned IP list:

Notice the “File list” log file location, in this case /var/log/auth.log, which we need later when testing our jail.

The response should start with “Status for the jail: wordpress-auth” and show you some statistics (probably all 0 at this point) but if it sais “Sorry but the jail ‘wordpress-auth’ does not exist” then something has gone wrong.

If so, you can use this command to check the fail2ban service status and any errors that were encountered during the last restart:

sudo service fail2ban status

Or check for errors in the fail2ban log file right after doing a fail2ban restart:

tail -n 50 /var/log/fail2ban.log

3. Test your WordPress Auth jail

A. Test logging to auth.log

Open an incognito browser window and go to your WordPress site admin. Try to log in with a fake username and/or wrong password.

Then look at the last lines of the log file mentioned under “File list” (above) via ssh. There should be a line like this:

$ tail -n 50 /var/log/auth.log
...
wordpress[957319]: Failed authentication attempt with username dgf from xxxx:861:42c2:a300:29fd:7a0b:8127:5420 (Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36)
...

B. Check if fail2ban sees failed attempts

The fail2ban client status should report at least one failed attempt at this point:

$ sudo fail2ban-client status wordpress-auth
Status for the jail: wordpress-auth
|- Filter
|  |- Currently failed:	1
|  |- Total failed:	1
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	0
   |- Total banned:	0
   `- Banned IP list:

C. Verify lockout after X failed attempts

To test your new jail without locking yourself out of your WordPress admin, you can use a VPN to get a temporary IP address. Then trigger multiple failed login attempts via your WordPress wp-login.php.

When you exceed the limits that you set in your configuration above, you should notice that your browser does not receive a response anymore and times out after a while.

Please note: Your browser may connect via IPv6 at first and then after getting locked out, re-connect via IPv4. Or the other way around. In any case, you may need to break the limit twice, once for IPv6 and once for IPv4 before you get locked out completely.

Verify what was logged via ssh:

$ sudo fail2ban-client status wordpress-auth
Status for the jail: wordpress-auth
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	9
|  `- File list:	/var/log/auth.log	
`- Actions
   |- Currently banned:	2
   |- Total banned:	2
   `- Banned IP list:	xxxx:6ea0:d802:5518::15 xxx.22.88.144
$ tail /var/log/fail2ban.log
2025-06-01 20:53:40,755 fail2ban.filter         [945227]: INFO    [sshd] Found xxx.228.4.116 - 2025-06-01 20:53:40
2025-06-01 20:53:58,666 fail2ban.filter         [945227]: INFO    [wordpress-auth] Found xxx.22.88.144 - 2025-06-01 20:53:58
2025-06-01 20:54:08,528 fail2ban.filter         [945227]: INFO    [sshd] Found xxx.92.176.182 - 2025-06-01 20:54:08
2025-06-01 20:54:31,028 fail2ban.filter         [945227]: INFO    [sshd] Found xxx.228.180.12 - 2025-06-01 20:54:30
2025-06-01 20:54:31,336 fail2ban.actions        [945227]: NOTICE  [sshd] Ban xxx.228.180.12
2025-06-01 20:54:59,693 fail2ban.filter         [945227]: INFO    [wordpress-auth] Found xxx.22.88.144 - 2025-06-01 20:54:59
2025-06-01 20:55:06,528 fail2ban.filter         [945227]: INFO    [wordpress-auth] Found xxx.22.88.144 - 2025-06-01 20:55:06
2025-06-01 20:55:06,975 fail2ban.actions        [945227]: NOTICE  [wordpress-auth] Ban xxx.22.88.144
2025-06-01 20:55:14,529 fail2ban.filter         [945227]: INFO    [sshd] Found xxx.68.126.207 - 2025-06-01 20:55:14
2025-06-01 20:55:14,558 fail2ban.actions        [945227]: NOTICE  [sshd] Ban xxx.68.126.207

Going further…

Want to go further protecting your WordPress site? Stay tuned for extended Fail2Ban strategies on this site…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.