Mitigate DDoS attack with ngx_http_limit_req_module and fail2ban

The fail2ban do have comprehensive collection of scripts that scan log files and ban IPs that match malicious activities. But we are going to look on how to use ngx_http_limit_req_module logs to ban IPs that shows sign of Distributed Denial of Service (DDoS) attack on your website.

It is assumed in this tutorial that Nginx server is installed in your server. The following procedures are tested on my Linode server running Centos 7 64-bit Linux distribution.

  1. Enable ngx_http_limit_req_module by adding the following script in your Nginx configuration:
    http {
      limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
      server {
        limit_req zone=one burst=5;
  2. Restart Nginx server:
    systemctl restart  nginx.service

    You will see entry something like this in Nginx error log if there's abuse detected:

    2015/08/27 02:18:05 [error] 21235#21235: *326 limiting requests, excess: 5.297 by zone "one", client:, server:, request: "GET /node/8 HTTP/1.1", host: "", referrer: ""

    We will use this sample log entry for our fail2ban filter script.

  3. Install fail2ban:
    yum install -y fail2ban
  4. Create fail2ban filter script based on the Nginx error log entry:
    vi /etc/fail2ban/filter.d/nginx-ddos.conf

    The content of this filter file:

    failregex = limiting requests, excess:.* by zone.*client: 
    ignoreregex = 
  5. We will use the /etc/hosts.deny to block the IP of the DDoS attacker so we will need to create new fail2ban action script:
    vi /etc/fail2ban/action.d/hostsdeny.conf

    Add the following script as its content:

    actionstart = 
    actionstop = 
    actioncheck = 
    actionban = IP= &&
                printf %%b ": $IP\n" >> 
    actionunban = IP= && sed -i.old /ALL:\ $IP/d 
    file = /etc/hosts.deny
    daemon_list = ALL
  6. Enable the newly created fail2ban filter:
    vi /etc/fail2ban/jail.local

    Append the following script:

    enabled = true
    port    = http,https
    banaction = hostsdeny
    findtime = 120
    bantime  = 7200
    maxretry = 30
    logpath = %(nginx_error_log)s
  7. Start the fail2ban service:
    systemctl start fail2ban
    systemctl enable fail2ban.service
    systemctl list-unit-files | grep fail2ban
  8. To check the status of this fail2ban filter:
    fail2ban-client status nginx-ddos

    You will see something like this:

    Status for the jail: nginx-ddos
    |- Filter
    |  |- Currently failed: 18
    |  |- Total failed:     770
    |  `- File list:        /var/log/nginx/nginx_error_log
    `- Actions
       |- Currently banned: 1
       |- Total banned:     8
       `- Banned IP list:
  9. To test if the fail2ban nginx-ddos filter working:
    fail2ban-regex /var/log/nginx/nginx_error_log /etc/fail2ban/filter.d/nginx-ddos.conf
  10. You can use apache-bench to test the whole system:
    ab -n 20 -c 10

    Execute the following command to monitor the fail2ban log:

    watch -n 1 tail -n 20 /var/log/fail2ban.log

    And you will something like this while testing with apache-bench:

    2015-08-29 09:56:45,535 fail2ban.filter         [16931]: INFO    [nginx-ddos] Found
    2015-08-29 09:56:45,536 fail2ban.filter         [16931]: INFO    [nginx-ddos] Found
    2015-08-29 09:56:45,537 fail2ban.filter         [16931]: INFO    [nginx-ddos] Found
    2015-08-29 09:56:45,538 fail2ban.filter         [16931]: INFO    [nginx-ddos] Found


I landed on your website while searching for instructions on how to properly configure limit_req_zone; I have it enabled on my own server, but apparently, it was being too permissive, and I needed to understand how to tweak it to stop those bots, crawlers, and fake login attempts/fake REST calls to grab so many resources (without banning them!).

To my delight, you not only explain that thoroughly, but you add, as a bonus, full integration with fail2ban! Ironically, I have fail2ban activated, and I even had the skeleton of a filter to look through the logs in a way very similar to yours, but... that filter wasn't even active and needed some hard kicking to do its job properly...

Thanks to this article, now I understand why fail2ban was not really doing much about those excessive requests. Every day I had to log in manually and (temporarily) ban all abusive requests. Obviously, I wouldn't catch them all — that's why we have cool things such as fail2ban. But it's important to understand how these tools actually work together to do its magic.

Again, my heartfelt thanks for pointing me in the right direction! I've learned quite a lot today, just from reading this...

Very good, works fine, thank you for taking the time to share this fail2ban tutorial on the internet

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.