Fail2Ban is the simplest security tool you can install on a Linux server. It watches log files for suspicious activity — repeated failed logins, exploit scanning, excessive requests — and temporarily bans the offending IP addresses using iptables. It is not a substitute for a WAF or proper authentication, but it stops the endless background noise of automated attacks from filling your logs and consuming resources.
How Fail2Ban works
- A filter defines a regex pattern to match in a log file
- A jail combines a filter with an action (usually banning the IP)
- Fail2Ban tails the log file. When an IP matches the filter too many times within a window, it triggers the action
- After a configurable time, the ban expires
Installation
# Debian/Ubuntu
sudo apt update
sudo apt install fail2ban
# Create a local config (never edit the default config files directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Always customise jail.local, not jail.conf. Updates overwrite jail.conf.
Basic configuration
# /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16
bantime = 3600
findtime = 600
maxretry = 5
destemail = your@email.com
action = %(action_mwl)s
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400
- bantime: How long an IP stays banned (seconds)
- findtime: The window in which repeated failures count (seconds)
- maxretry: Number of failures before banning
- action_mwl: Ban + send email with whois info and log lines
For SSH, 3 failures in 10 minutes = 24-hour ban is a good starting point. Bots move on quickly.
WordPress brute force protection
Create a custom filter for WordPress login attempts:
# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login\.php
^<HOST> .* "POST /xmlrpc\.php
ignoreregex =
# In jail.local
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600
This watches Nginx access logs for repeated POSTs to login and xmlrpc endpoints. Works with Apache access logs too — just point logpath to the right file.
WordPress auth filter with wp-login.php failures
For a more precise filter that catches actual login failures (not just access):
# /etc/fail2ban/filter.d/wordpress-auth.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login\.php HTTP.* 200
This only triggers on successful POSTs that result in HTTP 200 — login page reloads after a failed attempt. False positives are minimal with this approach.
Custom jails for web applications
Rate limiting API endpoints
# /etc/fail2ban/filter.d/api-abuse.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST) /api/.* HTTP.* (429|403)
ignoreregex =
[api-abuse]
enabled = true
filter = api-abuse
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 60
bantime = 7200
Generic 404 scanner protection
# /etc/fail2ban/filter.d/nginx-404.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) .* HTTP.*" 404
ignoreregex =
[nginx-404]
enabled = true
filter = nginx-404
logpath = /var/log/nginx/access.log
maxretry = 20
findtime = 60
bantime = 3600
Be careful with 404 jails — a misconfigured site with broken internal links can generate legitimate 404s.
Monitoring Fail2Ban
# Check status of all jails
sudo fail2ban-client status
# Check a specific jail
sudo fail2ban-client status sshd
# See currently banned IPs
sudo fail2ban-client banned
# Check the log
sudo tail -f /var/log/fail2ban.log
# Unban an IP (your own, if locked out)
sudo fail2ban-client set sshd unbanip 192.168.1.100
Common issues
1. Fail2Ban not starting
sudo fail2ban-client -d # Dry run — shows config errors
sudo journalctl -u fail2ban --no-pager | tail -20
2. Jails not banning
Check the log path is correct and the filter regex matches:
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress.conf
3. Can’t connect after getting banned
If you lock yourself out:
- Connect via out-of-band console (VPS provider’s web console)
sudo fail2ban-client set sshd unbanip YOUR_IP- Or: whitelist your static IP in
ignoreip
4. Logrotate breaks Fail2Ban
When logs rotate, Fail2Ban can lose its position. Use logpath with globs:
logpath = /var/log/nginx/access.log
/var/log/nginx/access.log.1
Security philosophy
Fail2Ban stops automated attacks. It does not stop:
- Targeted attacks from distributed IPs
- Credential stuffing (one attempt per account per IP)
- Zero-day exploits
Fail2Ban is one layer. Pair it with:
- SSH key-only authentication (disable password auth)
- Rate limiting at the application level
- A WAF for web application attacks
- Regular security updates
Quick start: the 5-minute setup
# Install
sudo apt install fail2ban -y
# Configure SSH jail
sudo tee -a /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
maxretry = 3
bantime = 86400
findtime = 600
EOF
# Start and enable
sudo systemctl enable --now fail2ban
# Verify
sudo fail2ban-client status sshd
That single SSH jail blocks 99% of the background noise on a public server. Everything else is incremental improvement.