How to Install and Configure Fail2ban for SSH and Nginx Protection
- Essential First Steps to Secure a Linux VPS
-> How to Install and Configure Fail2ban for SSH and Nginx Protection
- Moving from Passwords to SSH Keys: The Final Security Layer
Disclaimer: Host-Level vs Edge Security
Note: This guide helps you configure host-level security. Fail2ban is an excellent tool for stopping brute-force attacks, script kiddies, and aggressive crawlers locally. However, it is not a complete alternative to Edge Security services or Web Application Firewalls (WAF) such as Cloudflare, AWS Shield, or Akamai.
For high-traffic production websites, creating thousands of firewall rules dynamically (which Fail2ban does) can impact server performance. For "cheap and cheerful" VPS protection, Fail2ban is perfect. For enterprise-grade protection, consider using a dedicated WAF.
Introduction
Is your Virtual Private Server (VPS) constantly under SSH brute-force attacks? Or perhaps "bad bots" are crawling your site, looking for vulnerabilities like /admin, /wp-login.php, or .env files?
The solution is Fail2ban.
Fail2ban is an intrusion prevention software framework that protects computer servers from brute-force attacks. It scans log files (e.g., /var/log/auth.log or /var/log/nginx/error.log) and bans IPs that show malicious signs—too many password failures, seeking exploits, etc.—by updating system firewall rules to reject new connections from those IP addresses.
Step 1: Install Fail2ban
Before installation, it is always a good practice to update your package repository.
Update lists and install the package (Debian/Ubuntu):
xinit@localhost:~$ sudo apt update && sudo apt install -y fail2ban
If you are using CentOS/RHEL/AlmaLinux, you need the EPEL repository first:
xinit@localhost:~$ sudo yum install -y epel-release && sudo yum install -y fail2ban
Start the service and enable it to run on boot:
xinit@localhost:~$ sudo systemctl enable --now fail2ban
Step 2: Global configuration
Fail2ban installs with a default configuration file named jail.conf. Do not edit this file directly, as it will be overwritten during updates. Instead, we create a copy called jail.local.
Create the local configuration file:
xinit@localhost:~$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Now, let's configure the default behavior. We need to set the ignoreip (IPs that should never be banned) and the ban duration.
Open the file for editing:
xinit@localhost:~$ sudo nano /etc/fail2ban/jail.local
Find the [DEFAULT] section and adjust the settings. Crucial: Add your own home/office IP address to ignoreip so you don't lock yourself out!
[DEFAULT]
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts.
# ALWAYS add your own IP here (e.g., 123.123.123.123)
ignoreip = 127.0.0.1/8 ::1 your_external_ip_address
# "bantime" is the number of seconds that a host is banned.
# 3600 = 1 hour. 86400 = 1 day.
bantime = 3600
# A host is banned if it has generated "maxretry" during the last "findtime"
findtime = 10m
# "maxretry" is the number of failures before a ban occurs.
maxretry = 3
Step 3: Protecting SSH
The most important jail is for SSH. It monitors authentication logs for failed login attempts.
Scroll down to the [sshd] section in jail.local (or add it if missing) and ensure it is enabled:
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
Step 4: Protecting Nginx
Fail2ban comes with pre-defined filters for Nginx. We will enable three common protections: Authentication failures, "Bot Search" (scanners looking for scripts), and Bad Bots (User-Agents).
Add these blocks to your /etc/fail2ban/jail.local file.
Nginx HTTP auth
Bans IPs that fail to log in to password-protected areas of your site (Basic Auth):
[nginx-http-auth]
enabled = true
port = http,https
logpath = %(nginx_error_log)s
Nginx bot search
This acts as a trap for scanners. It looks for 404 errors on commonly scanned paths (like setup.php, admin/, wp-login.php). If an IP hits too many non-existent scripts, it gets banned.
[nginx-botsearch]
enabled = true
port = http,https
logpath = %(nginx_error_log)s
maxretry = 2
Step 5: Blocking bad User-Agents
Sometimes you want to block bots based on their "User-Agent" header (e.g., Python scripts, Go-http-client, or known scraping tools).
First, create a filter definition. Create a new file /etc/fail2ban/filter.d/nginx-badbots-custom.conf:
xinit@localhost:~$ sudo nano /etc/fail2ban/filter.d/nginx-badbots-custom.conf
Paste the following definition. Warning: Be careful blocking curl, requests or Go-http-client if you use these tools for your own API interactions or health checks.
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
ignoreregex =
# Standard patterns for bad bots
badbotscustom = Go-http-client\/.*|python-requests\/.*|Python\/.*|python-httpx\/.*|Scrapy\/.*|lychee\/.*
Now, activate this jail in your jail.local:
[nginx-badbots-custom]
enabled = true
port = http,https
filter = nginx-badbots-custom
logpath = /var/log/nginx/access.log
maxretry = 1
bantime = 86400
Step 6: Apply and verify
After making changes to the configuration, restart the Fail2ban service.
Restart command:
xinit@localhost:~$ sudo systemctl restart fail2ban
Check the status of the server and listed jails:
xinit@localhost:~$ sudo fail2ban-client status
Check the detailed status of a specific jail (e.g., SSH), to see how many IPs are currently banned:
xinit@localhost:~$ sudo fail2ban-client status sshd
Conclusion
You have now added a significant layer of security to your VPS. Fail2ban will quietly work in the background, banning thousands of malicious IPs that attempt to guess your passwords or scan your Nginx server for vulnerabilities.