Banner image of Web server security – Part 4: WAF ModSecurity and IPS Fail2ban

Web server security – Part 4: WAF ModSecurity and IPS Fail2ban

In the previous part of this series, we talked about TLS, OCSP and security-relevant HTTP response headers. In this part, we introduce the web application firewall ModSecurity and the intrusion prevention system Fail2ban.

Always stay in the loop!
Subscribe to our RSS/Atom feeds.


  • hardened web server as configured in part 2
  • SSH client on your computer
  • basic knowledge about firewalls


All of the three components in this guide have different purposes. However, all of them are needed to accomplish our goal: Blocking attackers, bots and other malicious activities automatically.


Fail2ban is an intrusion prevention system. It monitors different log files of the server to block suspicious IPv4 or IPv6 addresses (e.g., too many failed login attempts, bot-like scanning for certain file types, usage of denied HTTP methods). Administrators can use regular expressions to customize standard configuration (so-called “jails”).


ModSecurity is an open-source web application firewall originally developed for Apache web server. In part 2 of our guide, we already installed ModSecurity to hide our server signature. We can configure “SecRules” to log and filter HTTP traffic in real-time. ModSecurity will be the foundation for blocking unwanted IP addresses. It is also possible to use ModSecurity with Nginx.

OWASP ModSecurity CRS

The ModSecurity Core Rule Set is developed by OWASP (Open Web Application Security Project). This rule set can be used by ModSecurity to provide protection against common attacks and vulnerabilities in web applications. We use CRS to add some useful rules to ModSecurity.


In this section, we install and configure Fail2ban and OWASP ModSecurity CRS. Finally, we tell ModSecurity to use the CRS.


First of all, download and install Fail2ban using apt: sudo apt install fail2ban. After this, we duplicate the default configuration file of Fail2ban: sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local as recommended.

Open the fresh copy using nano editor: sudo nano /etc/fail2ban/jail.local.

In “jail.local,” look for the following snippet and add enabled = true:

# Add the following line
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s

This tells Fail2ban to monitor the “/var/log/auth.log” file and temporarily block IP addresses that continuously try to log in using SSH while providing no valid credentials.

Also look for:


port     = http,https
logpath  = %(apache_error_log)s
maxretry = 2

Change this to:

enabled  = false
port     = http,https
logpath  = %(apache_error_log)s
bantime  = 86400
maxretry = 1

“bantime = 86400” means that IP addresses will be banned for 86400 seconds (= 24 hours). We will change “enabled = false” to “enabled = true” later.

Finally, restart Fail2ban: sudo systemctl restart fail2ban. If necessary, enable and start it: sudo systemctl enable fail2ban && sudo systemctl start fail2ban.

Keep in mind that your version of Fail2ban may only support IPv4, so attackers using IPv6 can’t be blocked. On Debian 10, Fail2ban comes with support for IPv4 and IPv6 addresses.


Install ModSecurity: sudo apt install libapache2-modsecurity. If you implemented all steps of part 2, this results in:

libapache2-modsecurity is already the newest version

Now, remove “-recommended” from the “modsecurity.conf” file: sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf.

Open sudo nano /etc/modsecurity/modsecurity.conf and change:

  • “SecRuleEngine DetectionOnly” to “SecRuleEngine On” (enables ModSecurity)
  • “SecStatusEngine On” to “SecStatusEngine Off” (disables transmission of information about your ModSecurity setup to the developer team)

OWASP ModSecurity CRS

Installing CRS requires “git.” If you didn’t install it so far, install it now: sudo apt install git.

Clone the latest version of CRS: git clone

Enter the folder cd owasp-modsecurity-crs/ and remove “.example” from “crs-setup.conf”: mv crs-setup.conf.example crs-setup.conf.

Move all rules to the ModSecurity folder sudo mv rules/ /etc/modsecurity/ and also the config file sudo mv crs-setup.conf /etc/modsecurity/crs/crs-setup.conf.

After that, open sudo nano /etc/modsecurity/crs/crs-setup.conf and enable rules by uncommenting them. For example, we only allow the HTTP method GET by uncommenting and modifying rule id 900200:

SecAction \

Carefully check which rules are useful and enable them by uncommenting them. Document which rules you use. Afterward, save the file.

Now, open Apache’s configuration file for ModSecurity sudo nano /etc/apache2/mods-enabled/security2.conf and include the new files:

<IfModule security2_module>
        # Default Debian dir for modsecurity's persistent data
        SecDataDir /var/cache/modsecurity

        # Include all the \*.conf files in /etc/modsecurity.
        # Keeping your local configuration in that directory
        # will allow for an easy upgrade of THIS file and
        # make your life easier
        IncludeOptional /etc/modsecurity/*.conf
        Include /etc/modsecurity/crs/crs-setup.conf
        Include /etc/modsecurity/rules/*.conf

        # Include OWASP ModSecurity CRS rules if installed
        # IncludeOptional /usr/share/modsecurity-crs/owasp-crs.load

        SecServerSignature " "

Restart Apache web server: sudo systemctl restart apache2. Keep in mind that CRS is also subject to change. So, it is best to check if there are any updates for rules used by your web server from time to time.


After restarting Fail2ban and Apache, you can test ModSecurity before turning on Fail2ban’s ModSecurity rule: Open a web browser on your client and enter “[your-domain]/index.html?exec=/bin/bash”.

Then open Apache’s “error.log” on your server: sudo tail -f /var/log/apache2/error.log. There must be an event in the log file that was created due to your test above.

Finally, enable Fail2ban’s ModSecurity rule. Open sudo nano /etc/fail2ban/jail.local, look for “[apache-modsecurity]” and change “enabled = false” to “enabled = true.” Save the file and restart Fail2ban: sudo systemctl restart fail2ban.

If you want to test Fail2ban, use the Tor Browser. By doing so, you avoid that your own real IP address gets banned by Fail2ban. You can unban a particular IP address by entering: sudo fail2ban-client set [jail-name] unbanip [ip-address].

If you want to unban all IP addresses at once, get the path of Fail2ban’s database file: sudo fail2ban-client get dbfile. Then stop Fail2ban sudo systemctl stop fail2ban and delete its database rm [path]. Start Fail2ban again: sudo systemctl start fail2ban.

Important: Some websites report that Fail2ban’s regular expression to match errors of ModSecurity is broken. If you also experience this, open sudo nano /etc/fail2ban/filter.d/apache-modsecurity.conf and look for “failregex = […].”

Change the regex to: failregex = ^%(_apache_error_client)s(?: \[client [\d\.:]+\])? ModSecurity:\s+(?:\[(?:\w+ \"[^\"]*\"|[^\]]*)\]\s*)*Access denied with code [45]\d\d (?:.*)$.

Follow us on Mastodon:

Continuous monitoring

As always, it isn’t sufficient to just install and enable security software and hope for the best. The most important part is continuous monitoring and testing of your setup. Test regularly if Fail2ban still bans IP addresses as expected. You can use the following commands to get information about your jails:

  • sudo fail2ban-client status apache-modsecurity (actions due to ModSecurity’s error files)
  • sudo fail2ban-client status sshd (actions due to SSH’s error files)

Additionally, regularly check all error log files of your server.

This article is part of the Web server security series.
Read other articles of this series.


Fail2ban and ModSecurity allow you to get rid of basic attacks, bots and IP addresses that show malicious behavior. Carefully check all CRS rules and your setup over and over again to ban everything you don’t want on your server. Moreover, regularly test your setup and check the status of Fail2ban and ModSecurity. Finally, check if there are updates for CRS from time to time.

In the fifth part of this series, we show you basic server-side DNS security configuration.

Read also