Fail2Ban Custom Blocking Rules Based on Nginx Status Codes (404/403 Threshold Optimization)


2 views

When implementing Fail2Ban with Nginx, we often need granular control over blocking behaviors based on different HTTP status codes. The standard configuration treats all failed requests equally, but in reality:

  • 404 (Not Found) errors might indicate scanning attempts
  • 403 (Forbidden) errors often suggest actual attack patterns
  • Different thresholds should apply based on threat severity

We'll create separate Fail2Ban filters and jails for each status code pattern:

# /etc/fail2ban/filter.d/nginx-404.conf
[Definition]
failregex = ^.*"GET.*HTTP/.*" 404 .*$
ignoreregex =

# /etc/fail2ban/filter.d/nginx-403.conf
[Definition]
failregex = ^.*"GET.*HTTP/.*" 403 .*$
ignoreregex =

Now configure separate jails in /etc/fail2ban/jail.local:

[nginx-404]
enabled = true
filter = nginx-404
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 300
bantime = 86400

[nginx-403]
enabled = true
filter = nginx-403
logpath = /var/log/nginx/error.log
maxretry = 3
findtime = 300
bantime = 86400

For more sophisticated detection, combine multiple conditions:

# Detect repeated 404s to specific sensitive paths
failregex = ^.*"(GET|POST).*(wp-admin|config|\.env).*HTTP/.*" 404 .*$

# Detect 403s with specific attack patterns
failregex = ^.*"(GET|POST).*(\.php|\.asp|cmd\.exe).*HTTP/.*" 403 .*$

Always test new filters before deployment:

fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-404.conf
fail2ban-client status nginx-404
fail2ban-client set nginx-404 unbanip 1.2.3.4

When implementing multiple jails:

  • Monitor system resources with fail2ban-client status
  • Consider log file rotation impact
  • Adjust findtime based on your traffic volume

When using Fail2Ban with Nginx, the default configurations typically trigger IP blocking based on generic failed login attempts or brute-force patterns. However, web applications often need more granular control - particularly when dealing with different HTTP status codes. A 404 (Not Found) might indicate scanning attempts, while 403 (Forbidden) could signal unauthorized access attempts.

We'll create separate Fail2Ban filters and jails for each status code pattern:


# /etc/fail2ban/filter.d/nginx-404.conf
[Definition]
failregex = ^ .* "(GET|POST|HEAD).*HTTP.*" 404 .*$
ignoreregex =

# /etc/fail2ban/filter.d/nginx-403.conf  
[Definition]
failregex = ^ .* "(GET|POST|HEAD).*HTTP.*" 403 .*$
ignoreregex =

In your jail.local (or jail.d/nginx.conf):


[nginx-404]
enabled  = true
port     = http,https
filter   = nginx-404
logpath  = /var/log/nginx/error.log
maxretry = 10
findtime = 300
bantime  = 86400

[nginx-403]
enabled  = true
port     = http,https  
filter   = nginx-403
logpath  = /var/log/nginx/error.log
maxretry = 3
findtime = 300
bantime  = 86400

Always verify your regex patterns before applying:


fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-404.conf

For more complex scenarios, you can combine multiple conditions:


# Block IPs hitting specific URLs with 404s
failregex = ^ .* "(GET|POST).*/wp-admin.*HTTP.*" 404

When monitoring multiple status codes:

  • Use separate log files for different virtual hosts
  • Consider log rotation settings
  • Monitor Fail2Ban's memory usage

Remember to restart Fail2Ban after configuration changes:

systemctl restart fail2ban