How We Secured Our VPS After a Bot Attack: A Complete Guide to UFW, Nginx Hardening & Fail2Ban

When you run a busy website, the server becomes more than just a machine — it starts feeling like a living part of your day. And nothing stresses that relationship faster than waves of unwanted bot traffic. It happened to us recently on dtptips.com, and the experience pushed us into some of the most meaningful VPS hardening work we’ve done so far.

This guide walks through every command we used — but more importantly, it explains what those commands actually do and why they matter. Security becomes much easier when you understand the reasoning behind every change.

Let’s start with the foundation: your firewall.


Building the First Shield: UFW Firewall with Proper Explanations

Before touching web server configuration or bot filtering, we strengthened the first layer of protection — the firewall. UFW (Uncomplicated Firewall) is Ubuntu’s built-in tool that decides who is allowed to talk to your server and who isn’t.

Think of UFW as a bouncer who checks IDs before letting anyone inside.

Let’s explore the exact steps we followed, along with what each one really means.


Allowing SSH access

sudo ufw allow OpenSSH

What this means:
This rule allows incoming connections to the SSH service, which is how you log in to your VPS via terminal.

Why this matters:
If you accidentally block SSH, you lose access to your own server. This command ensures SSH remains open before applying stricter rules.


Allowing web traffic for your site

sudo ufw allow 'Nginx Full'

What this means:
This command opens port 80 (HTTP) and port 443 (HTTPS). These are the two roads through which all website visitors reach your server.

Why it matters:
Without this rule, your site becomes unreachable.
“Nginx Full” is a pre-defined UFW profile that safely opens exactly what your web server needs.


Moving SSH from port 22 to a safer custom port

After editing /etc/ssh/sshd_config to change the SSH port, we allowed that port:

sudo ufw allow 2222/tcp
sudo ufw deny 22/tcp

What this means:

  • The first command opens port 2222 — your new SSH entry point.
  • The second command blocks port 22 completely.

Why it matters:
Port 22 is scanned constantly by bots trying to brute-force servers.
By closing it and using a custom port, you remove yourself from thousands of automated attacks.

Real benefit:
Your logs stop filling with failed SSH attempts overnight.


Blocking harmful IP ranges

When we noticed repeated bot activity from certain networks, we blocked the entire range:

sudo ufw deny from 143.198.0.0/16
sudo ufw deny from 47.246.0.0/16
sudo ufw deny from 138.84.0.0/16

What this means:
These commands deny all traffic coming from those IP ranges.

Why it matters:
Sometimes attackers use entire datacenter networks. Blocking the whole block stops hundreds of IPs in one rule.

Good to know:
You should only block ranges when you’re sure legitimate users won’t be affected.


Activating UFW

sudo ufw enable
sudo ufw status numbered

What this means:

  • The first command turns the firewall on.
  • The second command displays all your rules neatly with numbers for easy removal.

Why it matters:
This is your final “lock the gate” moment.
Once enabled, your server immediately becomes harder to attack.


Strengthening the Gatekeeper: Nginx Hardening With Deep Explanations

Once UFW blocked unwanted traffic at the network level, Nginx became our second line of defense — the gatekeeper that decides how requests are handled once they reach the server.

Let’s explain each technique clearly.


Blocking known bad bots using a user-agent map

map $http_user_agent $bad_bot {
    default 0;
    "~*ahrefs" 1;
    "~*semrush" 1;
    "~*mj12bot" 1;
    "~*dotbot" 1;
    "~*spider" 1;
    "~*crawl" 1;
    "~*python" 1;
    "~*curl" 1;
    "~*wget" 1;
}

Then inside the server block:

if ($bad_bot) {
    return 403;
}

What this means:
This configuration checks the “user agent” string of each visitor. If the visitor identifies itself as a known bot, Nginx blocks access instantly.

Why it matters:
Scrapers often use simple tools like python, curl, or automated SEO bots that openly declare who they are. Blocking them at Nginx level reduces load dramatically.


Rate limiting login attempts

limit_req_zone $binary_remote_addr zone=loginlimit:10m rate=20r/m;

And:

location = /wp-login.php {
    limit_req zone=loginlimit burst=20 nodelay;
}

What this means:
Nginx allows each IP to attempt logging in only 20 times per minute.

Why it matters:
Brute-force attacks — where bots try thousands of passwords — instantly stop being effective.

Extra benefit:
Reduces PHP-FPM load, which keeps the server responsive.


Blocking PHP execution inside /uploads

location ~* ^/wp-content/uploads/.*\.php$ {
    deny all;
}

What this means:
Even if someone manages to upload a malicious .php file, it can’t be executed.

Why it matters:
Many WordPress hacks rely on disguising malware as an image.
This rule eliminates the entire attack vector.


Locking down the WordPress REST API

location ~ ^/wp-json/ {
    allow 127.0.0.1;
    deny all;
}

What this means:
Only local requests (your own server) can access the REST API.

Why it matters:
Bots abuse the API to enumerate users, brute-force endpoints, and scrape data.
If you don’t need external REST access, this is one of the strongest protections you can add.


Adding the Watchdog: Fail2Ban With Clear Explanations

Fail2Ban monitors logs and automatically bans abusive IPs. It works quietly, behind the scenes.


Installing Fail2Ban

sudo apt install fail2ban

What this means:
This installs the Fail2Ban service, which will monitor Nginx logs for suspicious patterns.

Why it matters:
Unlike UFW, Fail2Ban is dynamic — it reacts to behavior in real time.


Creating jail.local

sudo nano /etc/fail2ban/jail.local

What this means:
A dedicated config file that overrides defaults safely, without affecting system files.

Why it matters:
It keeps your custom rules separate and future-proof.


Nginx bad bot jail

[nginx-bad-bot]
enabled = true
port = http,https
filter = nginx-bad-bot
logpath = /var/log/nginx/access.log
maxretry = 1
bantime = 86400

What this means:

  • If the filter detects a bad user agent, the IP is banned for 24 hours.
  • Only 1 violation is needed to trigger the ban.

Why it matters:
Most harmful bots announce themselves accidentally, so this jail cleans them up instantly.


The filter file

sudo nano /etc/fail2ban/filter.d/nginx-bad-bot.conf

With:

failregex = <HOST> -.*"(GET|POST).*HTTP.*(python|curl|wget|bot|spider)"

What this means:
Fail2Ban scans Nginx logs for suspicious user-agent patterns.

Why it matters:
It catches scrapers that slip past Nginx’s map logic.


Restarting Fail2Ban

sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status nginx-bad-bot

What this means:
Your jails go live, and you can check real-time bans.

Why it matters:
This is the moment Fail2Ban becomes your automated security guard.


Disclaimer

This guide is for educational purposes only.
Server configurations vary depending on your hosting provider, software stack, and security requirements. Always create backups before making changes, as incorrect firewall or web server rules can result in downtime or accidental lockouts. Consult a professional if you are unsure.


Final Thoughts

Security is not a single command — it’s a journey of small, thoughtful improvements. Each step we took, from UFW to Nginx to Fail2Ban, created a layered defense system that transformed dtptips.com from being overwhelmed to being in full control again.

If you follow these steps with care, your own VPS can gain the same resilience — quiet, stable, and ready to face the next wave of unwanted bots.


#ServerSecurity #Fail2Ban #UFW #NginxHardening #BotProtection

Visited 11 times, 3 visit(s) today

Michael Turner

Michael Turner

Michael is a freelance tech educator from Canada, known for simplifying complex software workflows. He has taught digital literacy courses and written training material for corporate teams. His how-to guides focus on solving real problems across Windows, Linux, Android, and popular online tools.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.