How to Configure Fail2ban Custom Jail Filters for Nginx on Ubuntu

Public-facing web servers running Nginx are subject to continuous automated scanning: malicious bots probing for exposed .env configuration files, automated WordPress xmlrpc.php brute-force attacks, and credential-stuffing scripts hitting authentication APIs. While default Fail2ban installations include basic HTTP authentication filters, modern attack vectors bypass standard 401 Unauthorized codes, manifesting instead as rapid bursts of 404 Not Found or 403 Forbidden errors. Creating custom Fail2ban jail filters and regex rules enables Ubuntu administrators to detect and ban malicious IP addresses dynamically using kernel-level Netfilter/UFW firewall rules.

Architecture of Fail2ban Log Parsing and Jails

Fail2ban operates through modular configuration components residing in /etc/fail2ban/:

  • Filter Definitions (filter.d/): Contain Python regular expression patterns (failregex) that monitor specific Nginx access or error log signatures.
  • Jail Configurations (jail.d/): Bind a filter to target log files, define threshold criteria (how many failures within a time window), and specify the ban action (e.g., dropping packets via ufw or iptables-multiport).
  • Ban Parameters: findtime dictates the evaluation window, maxretry defines the violation limit, and bantime governs the duration malicious hosts remain blocked.

Creating a Custom Nginx Scanner Filter

To detect malicious probes searching for sensitive files and administrative panels, create a dedicated filter file:

sudo nano /etc/fail2ban/filter.d/nginx-malicious-scanners.conf

Append the regex definition:

[Definition]
failregex = ^<HOST> -.* "(GET|POST|HEAD) /(wp-admin|wp-login.php|.env|.git|phpmyadmin|xmlrpc.php|setup.php|pma) HTTP/.*" (403|404|444)
ignoreregex =

The tag <HOST> is a built-in Fail2ban regex macro matching IPv4 and IPv6 client addresses. When an IP requests any of the flagged path signatures resulting in a 403, 404, or 444 HTTP status, Fail2ban increments a strike against the host.

Creating a Custom Jail in /etc/fail2ban/jail.d/

To avoid modifying distribution defaults in jail.conf, deploy your custom jail snippet within jail.d/:

sudo nano /etc/fail2ban/jail.d/nginx-hardening.local

Add the jail specification:

[nginx-malicious-scanners]
enabled = true
port = http,https
filter = nginx-malicious-scanners
logpath = /var/log/nginx/access.log
banaction = ufw
findtime = 300
maxretry = 3
bantime = 86400

This configuration mandates that any client IP generating 3 scanner violations within a 5-minute (300 seconds) window is blocked immediately by UFW firewall rules for 24 hours (86,400 seconds).

Validating Custom Regex Syntax with fail2ban-regex

Always verify regular expressions against active web logs before reloading the daemon:

sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-malicious-scanners.conf

The diagnostic utility parses existing log lines, reporting total matched lines and active candidate IPs without triggering premature firewall bans.

Applying Configuration and Auditing Active Jails

Reload Fail2ban to activate the custom jail:

# Reload fail2ban daemon
sudo systemctl restart fail2ban

# Inspect active jail status
sudo fail2ban-client status nginx-malicious-scanners

The status report provides real-time operational statistics:

Status for the jail: nginx-malicious-scanners
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     48
|  \- File list:        /var/log/nginx/access.log
\- Actions
   |- Currently banned: 5
   |- Total banned:     14
   \- Banned IP list:   198.51.100.23 203.0.113.88 ...

To manually unban an IP during testing, run: sudo fail2ban-client set nginx-malicious-scanners unbanip 198.51.100.23. Deploying custom Fail2ban filters keeps Nginx responsive and shields web backends from brute-force exploitation.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.