Safeguarding Your Web Server: WAF, fail2ban, and Rate Limiting Explained

94% of logins on Cloudflare's network are bots. Discover how WAF, rate limiting, and fail2ban can thwart attacks before they bring down your website.

by Cleverson Gouvêa

Safeguarding Your Web Server: WAF, fail2ban, and Rate Limiting Explained

Protecting web servers from attacks is no longer just for large corporations: by 2026, anyone with a website, online store, or system running on their own VPS is a daily, automated target. In this guide, I'll show you the three layers we use at Agathas Web — WAF, fail2ban, and rate limiting — what each one blocks, how much it costs, and when it hinders more than it helps.

TL;DR

  • 94% of login attempts passing through Cloudflare's network originate from bots, according to the company's 2026 threat report. Your login form is likely being tested right now.
  • A WAF blocks malicious requests before they reach the server; rate limiting restricts volume per IP or route; fail2ban reads logs and bans persistent offenders. No single layer replaces another.
  • Cloudflare's Free plan already provides basic managed WAF, 5 custom rules, and 1 rate limiting rule — sufficient to start, but inadequate for a critical system.
  • The number one pitfall: fail2ban behind a proxy/CDN without real IP configuration bans Cloudflare itself, bringing down your site.
  • Without someone reviewing logs and adjusting rules, any protection becomes outdated. That's where managed hosting comes in.

Why safeguarding your web server against attacks has become routine, not an exception

Until a few years ago, discussions about attacks revolved around "what if a hacker targets my company?" Today, no one is specifically chosen. Bots scan entire IP ranges, test leaked passwords on any login screen they find, and unleash floods of requests against responsive targets.

Recent figures make this clear:

  • Cloudflare's H1 2026 DDoS threat report recorded 29.64 trillion HTTP DDoS requests mitigated during the half-year and 23.2 million network-layer attacks — approximately 5,343 per hour.
  • In the same report, Brazil surpassed the United States as the leading country of origin for DDoS attacks during the half-year (14.9% vs 13.4%). A significant portion of this traffic originates from infected machines within Brazil.
  • 90.6% of attacks last less than 10 minutes. By the time someone notices and raises a ticket, the damage has already been done.
  • The Verizon DBIR 2025 indicates that 88% of breaches via basic web application attacks involved stolen credentials, and brute force in this category surged from around 20% to 60% of incidents.
  • The Imperva Bad Bot Report 2025 estimates that malicious bots now account for 37% of all internet traffic, and total automated traffic has surpassed human traffic (51%).

In practice, safeguarding web servers against automated attacks is no longer optional: your entry-level VPS receives the same kind of bombardment as a large portal — just without anyone monitoring it. On the servers I manage, the access log of a newly published WordPress site shows attempts on /wp-login.php and /xmlrpc.php within hours, even before the site appears on Google.

The three types of attacks that bring down small business servers

Before choosing tools to protect your web server against these types of attacks, it's worth categorising the problem. In our experience with websites, systems, and Moodle environments, almost every incident of unavailability falls into one of these three groups.

Brute-force and credential stuffing

Brute force involves trying password after password until one works. Credential stuffing is the refined version: the bot uses email and password pairs leaked from other services, betting that the user has reused the password. The same 2026 threat report from Cloudflare indicates that 46% of human logins use credentials that have already appeared in breaches.

The risk isn't just invasion. Each login attempt consumes CPU: PHP spins up, queries the database, calculates password hashes. A thousand attempts per minute on a wp-login.php are enough to slow down a small VPS for legitimate customers.

Scraping and scanning bots

These are bots that don't want your password, but rather your content, your prices, or a known vulnerability. They traverse URLs like /.env, /.git/, /phpmyadmin, /wp-content/plugins/<plugin-vulnerable>/, and any path that has had a CVE published. Since 2025, AI crawlers have joined them, which can download thousands of pages in sequence.

Application DDoS (Layer 7)

Unlike volumetric DDoS, which attempts to saturate the link, application DDoS sends HTTP requests that appear legitimate — internal search, report page, shopping cart — specifically chosen because they are expensive for the server. A few hundred requests per second on the right route can bring down a system that would otherwise handle tens of thousands of accesses to a cached page.

The three layers for safeguarding web servers against attacks

Those who try to protect web servers against attacks often install one tool and stop. The most useful way to understand WAF, rate limiting, and fail2ban is by each one's position in the request path:

Layer Where it runs What it blocks best Weak point
WAF (Web Application Firewall) At the edge (Cloudflare) or on the server (ModSecurity + OWASP CRS) SQL injection, XSS, known CVE exploitation, identified bad bots False positives in legitimate forms; doesn't understand your business logic
Rate limiting At the edge and/or on Nginx/Apache and in the application itself Brute force, expensive route flooding, aggressive scraping Poorly calibrated limits block real users (corporate, school, 4G NAT)
fail2ban On the server, reading logs Persistent offenders: SSH, login, repeated scanning Reactive — acts after the log; useless if the registered IP is that of the proxy

The central point: to truly safeguard your web server against attacks, you need all three, because each covers the gaps of the others. The WAF doesn't know that IP X failed the password 40 times; fail2ban does. fail2ban doesn't see the traffic that the edge has already discarded; and rate limiting doesn't recognise an SQL injection signature.

WAF: The Entry Point Filter

A WAF is a firewall that understands HTTP. Instead of just looking at ports and IPs, it inspects the URL, headers, and request body, comparing them against known attack patterns.

Edge WAF: Cloudflare

This is the approach we use by default to protect web servers against application attacks. Requests pass through Cloudflare before reaching your server, and anything blocked there doesn't consume a single CPU cycle on your end. According to Cloudflare's official WAF documentation, the Free plan includes the Free Managed Ruleset (rules for high-impact vulnerabilities like Log4Shell and Shellshock) and 5 custom rules. The Pro plan costs US$ 20/month for annual payment (US$ 25 monthly) and unlocks the full managed set.

The most effective custom rules, in our experience:

  1. Managed challenge on administrative routes — /wp-admin, /wp-login.php, /admin — for those not originating from the UK, when the audience is solely national.
  2. Total blocking of /xmlrpc.php on WordPress sites that don't use mobile apps or Jetpack.
  3. Blocking paths that don't exist in your system and only appear during scanning: /.env, /.git/, /phpmyadmin.

On-Server WAF: ModSecurity and OWASP CRS

When traffic doesn't pass through a CDN, or the client requires rules within their own server, the open-source alternative is ModSecurity with the OWASP Core Rule Set (CRS). Two facts that have changed the landscape and that many old tutorials ignore:

  • Trustwave ended support for ModSecurity on 1st July 2024 and transferred project custody to OWASP.
  • F5 discontinued the commercial NGINX ModSecurity WAF on 31st March 2024.

The CRS remains active: the 4.25.x line is LTS, with security fixes planned until Q3 2027, according to the OWASP CRS project. It works, but requires fine-tuning — a CRS at a high paranoia level, installed without an observation period, will block contact forms, post editors, and file uploads on the first day.

When NOT to use WAF in blocking mode

Do not enable direct blocking on systems with extensive free text input (ERP, Moodle with forums and questionnaires, rich editors) without first running it for a few days in log only mode. In Moodle, for example, questions containing code snippets or SQL in a programming class can easily trigger injection rules.

Rate Limiting: How Much is Too Much?

Rate limiting is the component that most helps protect web servers against volume attacks. It involves counting requests per client within an interval and cutting off those who exceed the threshold. It sounds simple; the challenge is choosing the right number.

At the Edge

Since 2022, Cloudflare has offered rate limiting on all plans, but with very different limits. According to the rate limiting rules documentation:

  • Free: 1 rule, counting window up to 10 seconds, blocking up to 10 seconds, counting only by IP and filtering only by path.
  • Pro: 2 rules, window up to 1 minute, blocking up to 1 hour.
  • Business: 5 rules, window up to 10 minutes, blocking up to 1 day, and IP counting with NAT support.

With a single rule on the Free plan, use it on the login route. That's where the greatest gain is.

On the Web Server

In Nginx, the limit_req module does the job with the leaky bucket algorithm: you define the rate (e.g., 5r/m for /wp-login.php) and a tolerated burst with burst. In Apache, the equivalent is usually mod_evasive or ModSecurity's own rules. The important thing is to limit route by route: login and search with a low ceiling; static files, none.

In the Application

The most precise layer is the application itself, because it knows who the user is. In this site's administrative panel, we implemented a login attempt record in the database that blocks by account and by IP after a sequence of errors. Moodle has this natively: under Site administration › Security › Site policies, the account blocking parameters (attempt limit, window, and duration) are off by default — and almost no one turns them on.

Pitfall: School and Corporate NAT

An entire school, a call centre, or a 4G operator might access the internet via the same IP. We've seen EAD platforms crash an entire class's exam because a rate limit of 60 requests per minute per IP counted 40 students as 'one client'. Calibrate based on actual peak log data, not tutorial numbers.

fail2ban: Persistent Offenders Get Banned

The fail2ban reads log files, looks for failure patterns (wrong SSH password, 401 on login, serial 404s), and, upon exceeding the limit, creates a firewall rule banning the IP for a period.

Default Values (and Why to Change Them)

fail2ban is the oldest layer for protecting web servers against persistent attacks, but it comes with timid values. In the official jail.conf, the default is bantime = 10m, findtime = 10m, and maxretry = 5: five failures in ten minutes result in ten minutes of banning. For a bot, ten minutes is a coffee break. What we apply:

  • bantime.increment = true — commented out by default; when activated, each re-offence doubles the ban time.
  • Jail recidive — already included in the package and bans for 1 week anyone who has been banned multiple times in 1 day.
  • Specific jails for the application: nginx-limit-req (those who exceed Nginx's rate limit), filters for WordPress login and the system's own panel.
  • SSH without password, only with key — then fail2ban on SSH becomes a second line of defence, not the first.

The Pitfall That Brings Down Your Site: fail2ban Behind Cloudflare

Attempting to protect web servers against attacks with fail2ban behind a CDN without adjustment is the most common error we find on servers we take over for maintenance. With Cloudflare in front, the IP that reaches Nginx is Cloudflare's, not the visitor's. fail2ban reads the log, sees 'the same IP' failing passwords, and bans... Cloudflare. Result: the site goes offline for a segment of visitors, with no apparent error.

To avoid this:

  1. Configure Nginx's real_ip module (or Apache's mod_remoteip) to trust only the IP ranges published by Cloudflare and read the CF-Connecting-IP header. Do not use raw X-Forwarded-For — it can be forged.
  2. Even with the real IP in the log, banning via iptables on the server blocks nothing: the connection still comes from Cloudflare. Use fail2ban's Cloudflare action (which creates the rule via API at the edge) or close the origin to accept only Cloudflare.

What Doesn't Solve the Problem (and Costs a Lot)

Some measures to protect web servers against attacks appear in every checklist and deliver little:

  • Security plugin as the sole layer. In WordPress, the plugin runs within PHP: when it blocks, the server has already expended processing power. It helps, but won't withstand a flood.
  • Changing the SSH port and stopping there. Reduces log noise, but offers no protection against those performing full scans.
  • Blocking entire countries in the server firewall. IP lists by country change; if poorly maintained, they block real customers and don't stop Brazilian botnets — which, as we've seen, lead the rankings.
  • Buying a larger server to withstand attacks. Scaling vertically to absorb bots is paying to be attacked. Filtering at the edge is cheaper.
  • Configure and forget. An unreviewed WAF rule generates silent false positives; fail2ban without monitoring stops working when the log format changes in an update and no one notices.

If your server is in a public cloud, the cost of ignoring this also appears on your bill: outbound traffic and extra CPU from bots are charged as if they were from customers. I detailed this calculation in Moodle on AWS vs. Managed Hosting.

Practical Checklist for Safeguarding Your Web Server in 30 Days

For those who want to protect their web server against attacks on their own, this is the order we recommend — from the greatest risk reduction per hour invested to the least:

Week 1 — Edge

  • Place the domain behind Cloudflare (active proxy, orange cloud).
  • Close the origin: firewall accepting HTTP/HTTPS only from Cloudflare's IP ranges.
  • Activate the Free Managed Ruleset and create challenge rules for administrative routes.

Week 2 — Access

  • SSH with key only, root login disabled.
  • MFA on hosting panel, domain registrar, and system administrative accounts.
  • Account blocking by attempts in the application (Moodle, WordPress, custom panel).

Week 3 — Server

  • real_ip configured and tested (does the visitor's IP appear in the log?).
  • fail2ban with bantime.increment, recidive, and application-specific jails.
  • limit_req on login and search routes.

Week 4 — Observation

  • Review blocks: was the blocked entity truly a bot?
  • Test backup restoration. Protection fails; backup is Plan B. For Moodle, the step-by-step guide is in Moodle backup: the routine that saves the institution.
  • Define who receives alerts when something goes wrong — and how quickly they respond.

If you've read this far thinking "I don't have anyone to do this", that's the honest answer: without someone owning the subject, the checklist becomes a forgotten file.

How Agathas Web Solves This

At Agathas Web's managed hosting, the work of protecting web servers against attacks described in this post is not an optional extra: it comes configured by default and is maintained by those who operate the server. Specifically, what's included:

  • Cloudflare WAF with custom rules for your stack (WordPress, Laravel, Next.js, Moodle, ERP), automatic DDoS mitigation, and malicious bot blocking.
  • fail2ban, rate limiting, and operating system hardening, with the real IP correctly configured behind the proxy — the pitfall from the previous section won't occur.
  • Automatic Let's Encrypt SSL, mandatory MFA, and log auditing.
  • 24/7 monitoring with Grafana, Prometheus, Sentry, and UptimeRobot, and alerts to the technical team's WhatsApp.
  • Daily + incremental backup, encrypted (AES-256) and off-server, with restoration tested monthly.
  • Critical patches applied within 72 hours of public CVE, passing through staging.
  • Contractual SLA: 99.9% uptime, response within 15 minutes, and resolution within 2 hours for critical incidents — with penalties if we fail to deliver.
  • Monthly report with uptime, events, tested backups, and threats mitigated by the WAF.

How it works: It starts with a free diagnosis of your current infrastructure, delivered within 3 working days, with risks highlighted. If it makes sense to proceed, we migrate your site, database, emails, and DNS within 7 working days, with staging for validation and a rollback plan (the old environment remains intact for 30 days). Monthly contract, no long-term commitment, 30 days' notice to cancel.

How much it costs: Institutional websites and blogs average between £250 and £500 per month; e-commerce and SaaS, between £600 and £2,500 per month; setup from £800 to £3,500 depending on complexity. Moodle environments have their own offering — requirements are in Moodle 5.x server requirements.

You speak directly with the team operating your environment, via WhatsApp, without a call centre.

Conclusion: Protection is a Process, Not a Product

WAF, rate limiting, and fail2ban are inexpensive components — most are free. What costs is the operation: calibrating limits based on real traffic, noticing when a rule starts blocking legitimate customers, reacting within minutes when 90% of attacks last less than ten. To sustainably safeguard your web server against attacks, someone needs to be responsible for this every week.

If that person doesn't yet exist in your company, request a free diagnosis of Agathas Web's managed hosting: within 3 working days, you'll know where your server is exposed — and you can decide what to do about it.