UFW rate limiting stops a single IP address from opening new connections faster than a threshold you set. In practice that means an SSH password-guessing bot gets dropped after a handful of attempts, while your own open session keeps working. This guide adds limit rules to SSH and mail ports, shows you how to lower the default threshold, and verifies that blocked attempts land in the firewall log.
Before you start
You need:
- A Linux VPS running Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux/Rocky 8–9, with
sudoaccess. - Working SSH key login, plus access to the browser console in your VPS provider's control panel as a fallback. Rate limiting can lock you out if you reconnect too fast.
- Your own admin IP address, so you can whitelist it first.
Check the current rules before changing anything:
sudo ufw status verbose
sudo ufw status numberedWhitelist your admin IP so your own reconnects are never counted:
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp comment 'admin SSH'Warning: If you apply a limit rule to port 22 and then lose access, open the browser console in your VPS provider's control panel and either delete the rule or add an allow rule for your current IP. Do not reboot the server while you are locked out of SSH.
How ufw limit works
ufw limit tracks new connections per source IP. When a source exceeds the threshold — six new connections in 30 seconds in current UFW versions — further new connections from that IP are denied. Existing sessions are not touched, so an open SSH shell or a long-running transfer survives.
Two details matter:
- Only new connections are counted. A single SSH session opening many channels still counts as one connection.
- The rule covers IPv4 and IPv6 by default. UFW adds a v6 rule automatically unless you restrict the family.
Confirm the threshold for your version, since it has changed between releases:
man ufwTip: ufw limit is a blunt per-IP brake, not an authentication check. Pair it with key-only SSH and Fail2ban for real brute-force protection.
Add UFW rate limiting to SSH
Add the rule:
sudo ufw limit 22/tcp comment 'SSH rate limit'Expected output:
Rule added
Rule added (v6)If you use a non-standard SSH port, substitute it:
sudo ufw limit 2222/tcp comment 'SSH rate limit'Check that the rule is listed and the order is sane:
sudo ufw status numberedYou should see something like LIMIT IN 22/tcp for both v4 and v6. UFW evaluates rules top-down and stops at the first match, so an earlier ALLOW IN 22/tcp rule makes your limit rule dead weight. Remove or reorder it if that happens — see Delete, insert and reorder UFW rules safely.
Rate-limit other exposed ports
The same rule type works on any TCP port that accepts logins:
sudo ufw limit 587/tcp comment 'mail submission rate limit'
sudo ufw limit 465/tcp comment 'SMTPS rate limit'
sudo ufw limit 2222/tcp comment 'alternate SSH'Warning: Mail submission ports are shared by real users. An office behind one NAT gateway presents a single source IP, and busy clients can trip a 6-per-30-seconds limit. If you see legitimate mail clients timing out, whitelist that network with sudo ufw allow from 198.51.100.0/24 to any port 587 proto tcp instead of limiting it.
Do not rate-limit ports 80 and 443 with ufw limit. Browsers open many parallel connections per page and you will block normal visitors. Handle HTTP floods at the web server layer instead — see Implementing advanced rate limiting in Nginx using limit_req.
Custom thresholds with before.rules
When six connections in 30 seconds is too generous, add your own rule using the kernel's recent module. Back up the file first:
sudo cp /etc/ufw/before.rules /etc/ufw/before.rules.bakOpen it:
sudo nano /etc/ufw/before.rulesInside the *filter section, above the final COMMIT line, add:
# SSH brute-force rate limit: drop a source after 4 new connections in 60 seconds
-A ufw-before-input -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH_LIMIT
-A ufw-before-input -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name SSH_LIMIT -j DROPSave, then reload:
sudo ufw reloadThe first line records each new connection from a source IP in the SSH_LIMIT list. The second drops the connection once that IP has four entries inside a 60-second window. Adjust --seconds and --hitcount to taste.
Warning: A syntax error in before.rules stops the firewall from reloading. Keep the backup file, test with sudo ufw reload, and if it fails restore with sudo cp /etc/ufw/before.rules.bak /etc/ufw/before.rules && sudo ufw reload.
Newer UFW builds may use an nftables backend, where the recent match is not available. If your reload fails, check your distribution's UFW documentation for the backend in use, or stick with ufw limit plus Fail2ban for per-IP banning.
Check it works
Turn on logging if it is off:
sudo ufw logging on
sudo tail -f /var/log/ufw.logFrom a second machine, open a burst of TCP connections:
for i in $(seq 1 10); do nc -z -w1 203.0.113.10 22; doneIn the log you should see entries such as:
[UFW BLOCK] IN=eth0 OUT= SRC=198.51.100.25 DST=203.0.113.10 PROTO=TCP SPT=51234 DPT=22If you used the recent module, inspect the tracked list directly:
cat /proc/net/xt_recent/SSH_LIMITA source IP that keeps failing appears here with a rising hit count. Details on reading these lines are in Read and tune UFW firewall logs.
Troubleshooting
- You cannot reconnect over SSH. Your own rapid reconnects tripped the limit. Use the browser console in your VPS provider's control panel, add
sudo ufw allow from <your-ip> to any port 22 proto tcp, or remove the rule withsudo ufw delete limit 22/tcp. - Rule added but nothing is blocked. An earlier
ALLOWrule for the same port matches first. Runsudo ufw status numberedand delete or reorder the allow rule. Couldn't load match 'recent'on reload. Thext_recentkernel module is missing or the nftables backend is active. Trysudo modprobe xt_recent; if that fails, revert toufw limitand add Fail2ban.- Legitimate users behind one NAT IP get blocked. Raise
--hitcountand--secondsinbefore.rules, or whitelist that subnet for the port. - IPv6 attacks still succeed. Check for a matching v6 rule in
sudo ufw status verbose; see UFW and IPv6: make sure your rules cover both.
Next steps
- Rate-limit SSH and other ports with ufw limit for the simpler baseline rule set.
- Protect SSH from brute-force attacks with Fail2ban to ban repeat offenders across all services.
- Read and tune UFW firewall logs so you can see what is actually hitting your ports.
- Set up a UFW firewall on Ubuntu and Debian if you are starting from a default server.
