Wall art: Three strikes

Blocking repeating 403 Forbidden requests with Fail2Ban

The HTTP 403 Forbidden is issued by web servers when logins to web services fails or when visitors try to access files they shouldn’t. However, some visitors — well, bots for the most part — doesn’t always get the message and keep sending request after request. This can be part of a brute-force login attempt or other scripted attempts at gaining access to the system.

Although issuing the 403 response should be enough, you sometimes need to apply stronger tools to prevent bad actors from wasting your server capacity. Fail2Ban is a tool that reads your server logs and blocks repeating offenders using temporary firewall rules once unwanted behavior is identified.

This recipe assumes Fail2Ban is already installed albeit unconfigured and that the Apache web server is already running and generating logs. Either software can be found in the package repository of most Linux and *BSD distributions.

Fail2Ban requires a filter definition file and a jail configuration file. The former file instructs Fail2Ban what to look for in Apache’s log files and the second file instructs it on how to respond to matches.

To set up the filter file, create a file in /etc/fail2ban/filters.d/httpd-forbidden.conf containing the following:

failregex = <HOST> - - .*HTTP/[0-9]+(.[0-9]+)?" 403
            [client <HOST>] AH01797: client denied

Note that the above failregex directive consists of two separate regular expressions spread over two lines. The first filter regex matches 403 responses in the Apache access log, such as those generated by WordPress and other content systems following incorrect login attempts. The second filter regex will match against resources blocked by the server configuration itself using the Apache error log format. Both of these filters assume that the default logging format is used; you must adjust the filters if you use non-standard logging formats.

Before deploying the new filter in Fail2Ban, you can test them using the fail2ban-regex utility that comes included with Fail2Ban. Run the utility program with the first parameter pointing to the log file and the second pointing to the new filter file. You may have to adjust the paths to the log files depending on your system.

fail2ban-regex /var/log/httpd/access_log /etc/fail2ban/filter.d/httpd-forbidden.conf
fail2ban-regex /var/log/httpd/error_log /etc/fail2ban/filter.d/httpd-forbidden.conf

If your logs contains a few logged 403 responses, you should see the same number of matches after running the commands. Generate a few 403 responses so your logs actually have some example matches.

To set up the jail configuration file, create a file in /etc/fail2ban/jails.d/httpd-forbidden.conf containing the following:

enabled = true
filter = httpd-forbidden
backend = polling
logpath = /var/log/httpd/error_log
bantime = 3600
maxretry = 6
findtime = 1800
port = 80,443
banaction = iptables-multiport

The configuration options here are pretty self-explanatory. Use the filter we created previously (referred to by name and not file path), pull in the two standard Apache logs, and block any IP address with 6-requests within 30-minutes for one hour. The banaction is overwritten from the default to a multi-destination port rule targeting the two default web ports 80 and 443.

In case you accidentally lock yourself out you can login over SSH and unblock your own IP by running the following command (replacing the example IP address with your own):

fail2ban-client set httpd-forbidden unbanip

It’s important to include any address that will return a 403 response in the /robots.txt directive file. This will prevent good bots that honor the robots directives file like Bingbot and Googlebot from inadvertently being identified as bad bots and temporarily locked out of your website.

Sources: Fail2Ban 0.8 configuration documentation.

Feature image © 2012 Daniel Nebdal.


  1. On Fedora 24 it seems you can’t monitor 2 log files in the logpath directive. See the below errors. Do you know if there’s a way to specify both? When I just use the path to the error log fail2ban starts correctly.

    Oct 17 10:06:38 fail2ban-client[6931]: ERROR Error in action definition sendmail-whois-lines[name=httpd-forbidden, sender=”fail2ban”, dest=”root”, logpath=”/var/log/httpd/error_log
    Oct 17 10:06:38 fail2ban-client[6931]: ERROR Errors in jail ‘httpd-forbidden’. Skipping…
    Oct 17 10:06:38 systemd[1]: fail2ban.service: Control process exited, code=exited status=255
    Oct 17 10:06:38 systemd[1]: Failed to start Fail2Ban Service.
    — Subject: Unit fail2ban.service has failed

      1. That’s what I originally thought but when I remove the path to the 2nd log file (access) all starts well, no more errors. Fail2ban worked fine without this jail.

Leave a Reply

Your email address will not be published. Be courteous and on-topic. Comments are moderated prior to publication.