Preventing SYN Flood Attacks on Your Linux Server
Learn how to protect your Linux server from SYN flood attacks with firewall rules, kernel tweaks, and Fail2ban.
SYN flood attacks are one of those frustrating things that can quietly – or not so quietly – take your server down, fast. They work by flooding your server with a ton of fake connection requests, leaving it too busy to handle the real ones from actual users.
I ran into one of these attacks a while back while hosting a small WordPress site on a VPS. One day, the site started loading really slowly, and soon after, it became completely unreachable. Even SSH was lagging. When I checked the logs, there were tons of strange connection attempts. That’s when I realized it was a SYN flood.
In this guide, I’ll walk you through the steps I now use to prevent SYN flood attacks on my own servers. We’ll cover:
- Kernel tweaks to help the server handle floods more efficiently
- Firewall rules to drop suspicious connections early
- How to use Fail2ban to automatically block abusive IP addresses
These steps are lightweight, effective, and won’t interfere with normal traffic. By the end, you’ll have a solid setup that helps keep your server responsive and secure.
Looking for more security tips and server hardening guides? Join my newsletter to get practical advice and updates straight to your inbox!
Author's Note
Before we jump into the technical stuff, I wanted to share a bit of the "why" behind this guide.
I’ve always been into Linux server security, but SYN flood attacks pushed me to dig deeper. When I first started looking into ways to prevent them, I found tons of guides online – but most were either vague, overly simplified, or just plain outdated. Some tossed out a few iptables rules and called it a day. Others mentioned tools like Fail2ban but didn’t explain how to set them up properly.
That just didn’t cut it for me.
So I decided to piece things together myself – test different approaches, break things, fix them again, and eventually land on a setup that actually works.
What you’re reading now is the guide I wish I had when I started.
To really block these kinds of attacks, you need to understand the basics of how TCP works – especially the Three-Way Handshake – and how SYN floods try to break that process.
Also, it’s worth noting that while DoS attacks can be handled with the right setup, large-scale DDoS attacks are tougher to stop completely. That said, even basic protections can make your server a much harder target.
Finally, even if your hosting provider (like Hetzner) has some built-in DDoS protection, it often doesn’t catch the smaller, sneakier SYN floods aimed at your VPS.
That’s why it’s on you to add extra layers of security to your servers – to make it as difficult as possible for attackers to cause trouble.
What is a SYN Flood Attack?
To understand a SYN flood attack, it helps to first know how a normal TCP connection starts. It begins with something called a Three-Way Handshake:
- The client sends a SYN packet to the server, signaling a request to connect.
- The server replies with a SYN-ACK packet, acknowledging the request.
- The client then sends an ACK packet to complete the handshake.
Once this handshake is complete, the connection is established, and data can start flowing.
A SYN flood attack takes advantage of this handshake process by sending many SYN packets to the server – but the attacker never sends back the final ACK. The server responds with SYN-ACKs and waits, leaving these connections "half-open".
Each half-open connection uses up server resources. When too many pile up, the server gets overwhelmed and can’t handle new, legitimate connections. In extreme cases, this can cause the server to crash or become completely unresponsive.
SYN flood attacks can come from a single device (a DoS attack) or from many devices at once (a DDoS attack), often coordinated through a botnet.
Because these connections never fully open, this type of attack is sometimes called a "half-open attack".
How I Tested Everything
To experiment safely, I set up two Ubuntu 24.04 virtual machines on my MacBook – one to play the role of the "attacker". and the other as the "victim". Both were barebones servers, perfect for simulating what might happen on a real VPS.
I installed NGINX on the victim machine to keep port 80 open, then used tools like hping3 and ApacheBench to simulate traffic. While ApacheBench is typically a web server benchmarking tool, it can also flood a server with requests to mimic what happens during an attack.
The real star here was hping3
. With the command:
hping3 -S -p 80 --flood victim.ip
I was able to launch a SYN flood. The --flood
flag sends a constant stream of SYN packets without completing the TCP handshake – and sure enough, it didn’t take long before the victim server started lagging, then completely stopped responding. Even my SSH connection dropped.
To observe what was happening, I ran:
tcpdump -nn port 80
Watching those packets pile up in real time really showed how quickly a SYN flood can choke a server.
I also ran ApacheBench with a high number of requests to simulate excessive legitimate-looking traffic. The server's CPU and memory spiked fast. Obviously, no real user would hit the server that hard – so it was a good baseline to test defenses.
What Didn't Work
At this point, I hadn’t applied any protection – just raw, open NGINX on port 80.
I tried enabling UFW (Uncomplicated Firewall), but it didn’t do much. Its built-in rate limiting (ufw limit
) works okay for things like SSH, but it’s far too aggressive for a public web server. You don’t want to accidentally block real users just because they refreshed a few times.
I quickly realized I needed a better solution – something smarter, more flexible, and ideally automated.
Finding What Worked
After a lot of digging and testing, I built a setup that allowed a reasonable number of SYN packets per IP in a short window – and logged or dropped any excess traffic. This gave me the control I needed to separate real users from potential attackers.
Rather than just copy-pasting firewall rules from random websites, I treated them like building blocks. I tweaked, tested, broke things, fixed them, and eventually landed on a rule set that actually worked under load.
Then came Fail2ban. I configured it to monitor logs and ban IPs that triggered too many half-open connections. With a few tweaks, it worked well alongside UFW, automatically adding malicious IPs to the block list.
Once everything looked good locally, I tested the same setup on two real VPS servers (hosted on Hetzner). The difference was night and day – even under stress, the servers stayed responsive.
Kernel Settings: Tweaks That Help (a Bit)
Before jumping into firewall rules, I wanted to see if some kernel-level tuning could help – and while it doesn’t stop a SYN flood on its own, it definitely improves how the server handles things under pressure.
Enable Syncookies (If Not Already)
This one’s usually enabled by default, but it’s worth double-checking:
sudo sysctl net.ipv4.tcp_syncookies
If the result isn’t 1
, just open your sysctl.conf
file:
sudo vim /etc/sysctl.conf
Uncomment or add:
net.ipv4.tcp_syncookies = 1
Syncookies help the server manage half-open connections more efficiently – not bulletproof, but useful.
Tighten Reverse Path Filtering
By default, reverse path filtering is set to loose mode (2
), but switching to strict mode (1
) offers better protection against IP spoofing.
In /etc/sysctl.conf
, find or add:
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
This tells your server to drop packets that don’t come from reachable IPs, which helps filter out bogus traffic.
Optimize Connection Handling
These two extra parameters can make your server more resilient under load:
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 3
tcp_max_syn_backlog
– Increases the size of the queue that stores half-open connections.4096
is a good starting point, but for high-memory servers, you can safely go higher – up to16384
– to handle heavier traffic.tcp_synack_retries
– Reduces how long the server keeps waiting for an ACK response. Lowering this to3
(from the default5
) helps free up resources faster when connections don’t complete.
These tweaks help the kernel respond faster and stay available when the connection queue starts filling up.
Apply the Changes
Once you're done editing /etc/sysctl.conf
, save the file and reload the settings:
sudo sysctl -p
A quick reboot after that ensures everything takes effect properly.
Understanding UFW Rule Files
Since we’ll be using UFW (Uncomplicated Firewall) to help protect the server from SYN flood attacks, it’s important to understand how UFW handles rules under the hood.
Inside the /etc/ufw/
directory, you’ll find three key files:
before.rules
– for IPv4 trafficbefore6.rules
– for IPv6 trafficuser.rules
– for rules you add with UFW commands likeufw allow
Here’s what matters:
- The
before.rules
andbefore6.rules
files are processed first, before anything you manually allow via command line. That means rules here take priority, which makes them the best place to add protections like SYN flood filtering. - The
user.rules
file stores your typicalallow
/deny
commands but should not be edited directly – UFW manages this file automatically.
By understanding the order UFW uses to load rules, you’ll be better equipped to insert effective protections exactly where they’ll have the most impact.
Blocking Invalid Packets (Prepping for SYN Flood Protection)
Let’s lay the groundwork for defending against SYN flood attacks by blocking invalid TCP packets.
First, make sure UFW is enabled and that you won’t lock yourself out:
sudo ufw limit 22/tcp
sudo ufw enable
ufw limit
rule helps protect SSH by rate-limiting connections – it blocks IPs that try to connect too many times in a short period, which is useful against brute-force and flood attempts.Next, we’ll filter out shady packets – like those that don’t follow the standard TCP handshake (e.g. from Nmap or other scanners). Every legit TCP connection should start with just the SYN flag.
Here’s how to block invalid stuff early:
- Open both
before.rules
andbefore6.rules
- Add this section after the last
COMMIT
line:
*mangle
:PREROUTING ACCEPT [0:0]
-A PREROUTING -m conntrack --ctstate INVALID -j DROP
-A PREROUTING -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j DROP
COMMIT
Then reload UFW:
sudo ufw reload
What this does:
- The first rule drops anything marked as
INVALID
by the kernel’s connection tracker. - The second rule drops TCP packets pretending to start a connection but missing the proper SYN flag.
- We add this to the mangle table and the PREROUTING chain, so these packets are filtered as soon as they arrive – before they waste any more server resources.
This is a lightweight but powerful way to cut down on noise and bad traffic before it becomes a problem.
Exploring SYN Flood Mitigation Approaches with UFW
Now that we’ve covered the basics – including blocking obviously bad traffic – it’s time to put up a solid line of defense against SYN flood attacks.
We’ll use UFW, Ubuntu’s built-in firewall, which you’ve already enabled and configured to allow SSH traffic. Plus, dropping invalid packets in the previous step gave us a strong foundation to build on.
Since we’re using ufw limit 22/tcp
for SSH, it’s already got some protection built in. That said, if you want a more custom approach, the rules we’re about to go over can apply to any TCP service, including SSH.
Before settling on a robust, all-in-one solution, I experimented with a few different ways to protect my server from SYN flood attacks using UFW.
Each approach had its strengths but also some limitations – whether it was overly broad limits, excessive logging, or lack of persistence. These trials helped me understand what worked, what didn’t, and why a more refined solution was necessary.
Below, I’ll walk you through the three strategies I tested, starting with simple rate limiting and moving toward a dynamic blacklist configuration.
After that, I’ll share the final solution that combines the best of all three.
Approach 1: Basic SYN Flood Mitigation Using limit
In this first attempt, I used the limit
module to rate-limit incoming TCP SYN packets to port 80, protecting against SYN flood attacks.
The rules were added to the end of the /etc/ufw/before.rules
file, just before the final COMMIT
line:
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m limit --limit 10/second --limit-burst 20 -j ACCEPT
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m limit --limit 10/second --limit-burst 20 -j LOG --log-prefix "[UFW SYN Flood Detected] "
-A ufw-before-input -p tcp --syn --dport 80 -j DROP
What these rules do:
- Accept Rule: Allows up to 10 new SYN packets per second (with a burst of 20) to port 80.
- Log Rule: Logs any excess packets beyond this rate for monitoring.
- Drop Rule: Drops any packets that exceed the rate limit to prevent abuse.
These rules were added directly to the ufw-before-input
chain, which is processed before any user-defined rules.
This chain is part of the early stages of UFW’s firewall processing, meaning these rules are applied before incoming traffic reaches any services or applications running on the server. This ensures potentially harmful connections are filtered out as early as possible.
sudo ufw allow 80/tcp
. The before.rules
rules take precedence.For IPv6 support, add the same rules to /etc/ufw/before6.rules
, replacing ufw-before-input
with the ufw6-before-input
chain.
✅ Result:
After saving the changes, I reloaded UFW using sudo ufw reload
. I then simulated a SYN flood using hping3
, and the rules worked as expected.
Excess packets were dropped, and logs filled with the defined prefix showed clear evidence of blocked attack attempts. Despite the test flood, CPU usage remained stable, with only a slight increase.
⚠️ Limitation:
This approach was simple and effective, but it came with a major limitation. The limit
module enforces a global rate limit, meaning if one client exceeds the threshold, all others are affected.
This isn’t ideal for production environments with multiple users, so I moved on to a more flexible solution.
Approach 2: Per-IP Rate Limiting with hashlimit
While the first method successfully blocked SYN flood attacks, it introduced a serious limitation: the limit
module applies a global rate limit.
That means if a single IP address triggers the limit, all clients are affected – even legitimate ones. This clearly isn’t suitable for a production environment with many concurrent users.
To solve this, I replaced the limit
module with the more flexible hashlimit
module, which lets us apply rate limits on a per-IP basis. That way, only abusive IPs are throttled, while others continue to access the service normally.
Here are the updated rules added to the end of the before.rules
file, just before the final COMMIT
line:
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
What these rules do:
- Drop Rule: Drops SYN packets from a single IP if they exceed 10 per second (with a burst of 20).
- Log Rule: Logs these excess packets using the prefix
[UFW SYN Flood Detected]
for monitoring. - Accept Rule: Allows new SYN packets that haven’t exceeded the limit to reach the server.
Key Parameters Explained:
--hashlimit-mode srcip
: Applies limits individually for each source IP address.--hashlimit-srcmask 32
: Ensures the limit applies to each unique IP. If needed, this can be changed (like/24
) to group IPs by subnet.
✅ Result:
The server successfully blocked SYN flood attempts while allowing normal traffic to pass through. During testing with hping3
, I could still access the NGINX default page via curl
, confirming the server was not overwhelmed and legitimate traffic was unaffected.
⚠️ Limitation:
This approach does not track or remember attackers. If an IP backs off for a moment and resumes the attack, it will be treated as a fresh connection attempt.
There's no persistence or blacklist – just real-time rate limiting.
Approach 3: IP Blacklisting with recent
+ hashlimit
After implementing per-IP rate limiting, I wanted to go one step further – by blacklisting IPs that continuously misbehaved.
To do this, I combined the hashlimit
module with the recent
module. This setup lets the firewall track and remember IPs that exceed allowed limits and temporarily block them using a dynamic blacklist.
Here are the rules I added to the end of the before.rules
file, just before the final COMMIT
line:
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -m recent --name blacklist --set --rsource -j DROP
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
-A ufw-before-input -m recent --name blacklist --rcheck --seconds 300 --hitcount 1 --rsource -j DROP
-A ufw-before-input -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
What these rules do:
- Blacklist & Drop Rule: Drops packets from any IP that exceeds the rate limit and adds that IP to a temporary blacklist.
- Log Rule: Logs excess SYN packets for monitoring.
- Blacklist Check Rule: Drops packets from IPs already on the blacklist for 5 minutes.
- Accept Rule: Allows new, valid connections to port 80 that haven’t exceeded the threshold.
✅ Result:
During testing, my IP was automatically blacklisted after simulating a SYN flood using hping3
. While blacklisted, all packets from my IP were dropped, and I couldn't access the web server.
After 5 minutes of inactivity, access was restored – confirming that blacklist enforcement and timeout both worked correctly.
You can check which IPs are currently blacklisted using:
sudo cat /proc/net/xt_recent/blacklist
⚠️ Limitation:
While this setup worked better than global limits, I quickly ran into a few issues.
Since the blacklist created by the recent
module lives only in memory, all entries are wiped after a reboot. That means persistent attackers could just wait it out or come back with new IPs.
Also, because IPs are blacklisted immediately after crossing the threshold, there’s a chance of blocking legitimate users who briefly spike above the limit.
This approach definitely offered more control than before, but it didn’t scale well for handling more distributed or high-volume SYN flood attacks. It felt like a solid step forward, but not something I’d rely on as a long-term or standalone solution.
Final Solution: Combining Rate Limiting and Fail2ban
In this final solution, we combine per-IP SYN flood protection with persistent blacklisting and broader access control using Fail2ban.
The recent
module we used earlier only stores blacklisted IPs in memory, so all entries are lost upon reboot. While this isn't a major issue for many setups, Fail2ban offers a cleaner and more powerful alternative by:
- Persistently block IPs across reboots.
- Ban IPs only after repeated violations (e.g., 5 times), rather than on the first offense.
- Unban IPs manually or automatically after a set duration.
This hybrid setup gives us the best of both worlds: real-time traffic control through UFW, and intelligent, manageable banning via Fail2ban.
By configuring Fail2ban to watch /var/log/syslog
for entries matching our custom log prefix, we can automatically block any IP that triggers our rules repeatedly.
This approach avoids prematurely banning legitimate users who may accidentally trigger rate limits, while still stopping persistent attackers effectively.
Install and Configure Fail2ban
Install Fail2ban with:
sudo apt install fail2ban
Now create a custom filter so that Fail2ban knows what to look for in your logs. The log prefix we used in our UFW rules earlier was:
[UFW SYN Flood Detected]
So we’ll create a filter to catch that. Go to /etc/fail2ban/filter.d/
and create a file called synflood.conf
:
[Definition]
failregex = .*UFW SYN Flood Detected.*SRC=<HOST>.*DPT=\d+.*
ignoreregex =
This regex will match log entries that look like:
[UFW SYN Flood Detected] IN=... SRC=192.168.0.1 DPT=80 ...
This works for both port 80 and 443, so you don’t need to create separate filters.
Fail2ban uses "jails" to define what to monitor, how many times to allow it, and what to do when the threshold is exceeded.
Create a local copy of the jail configuration:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
This is important because you should not modify the jail.conf
file directly, as it may be overwritten during an update.
Edit jail.local
, and under the # JAILS
section, add:
[synflood]
enabled = true
filter = synflood
action = iptables[type=allports, name=synflood, chain=fail2ban, protocol=tcp]
logpath = /var/log/syslog
maxretry = 5
findtime = 600
bantime = 86400
We are creating a new jail called synflood
that will block IPs from accessing all ports on the server if they appear five times in the /var/log/syslog
file within ten minutes, for a duration of one day.
As you may have noticed, I’m using iptables
to block IPs instead of UFW, by adding them to a new chain called fail2ban
. The reason for this is that if we use UFW, it will block IPs at the user level by adding them to the user.rules
file. However, this won’t take effect because the before.rules
file is processed first.
You can also block traffic only on ports 80 and 443 by using the multiport
option, like this:
action = iptables[type=multiport, name=synflood, chain=fail2ban, port="http,https", protocol=tcp]
This is useful if you only want to restrict web traffic while leaving other services like SSH or FTP unaffected by the ban.
Update Firewall Rules
Next, we modify UFW’s before.rules
file so that:
- Fail2ban can inject blocking rules early in the firewall chain.
- We can isolate SYN flood protection into its own chain.
Edit /etc/ufw/before.rules
and create two new chains placing them under the # End required lines
line:
:SYN_FLOOD_PROTECTION - [0:0]
:fail2ban - [0:0]
Then replace/add these rules:
# Direct traffic to fail2ban first
-A ufw-before-input -j fail2ban
# fail2ban will insert IP block rules above this line
-A fail2ban -j RETURN
# Now send relevant traffic to our SYN_FLOOD_PROTECTION chain
-A ufw-before-input -p tcp --syn --dport 80 -j SYN_FLOOD_PROTECTION
-A ufw-before-input -p tcp --syn --dport 443 -j SYN_FLOOD_PROTECTION
# SYN rate-limiting and logging (HTTP)
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
# SYN rate-limiting and logging (HTTPS)
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 443 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 443 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
# Allow remaining SYN packets through
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 443 -m conntrack --ctstate NEW -j ACCEPT
🔍 What’s Happening Here?
- First, we redirect traffic from the
ufw-before-input
chain to thefail2ban
chain. - The rule
-A fail2ban -j RETURN
exits thefail2ban
chain and moves traffic to the next rule inufw-before-input
, which sends it to ourSYN_FLOOD_PROTECTION
chain. - In the
fail2ban
chain, Fail2ban dynamically inserts IP block rules when it detects malicious activity. - So, UFW checks the
fail2ban
chain first, and if the source IP is blacklisted, it’s immediately denied access. - Once it reaches the
RETURN
rule, traffic is passed along to theSYN_FLOOD_PROTECTION
chain, which:- drops traffic that exceeds our rate limits,
- logs it for Fail2ban to analyze,
- and allows traffic from IPs that are behaving normally.
before6.rules
the same way for IPv6 support – replacing ufw-before-input
with the ufw6-before-input
chain.This layered approach ensures blocked IPs are filtered before they hit your rate limit logic, making your firewall more efficient and more secure.
Reload UFW and Restart Fail2ban
After replacing/adding your firewall rules and setting up Fail2ban, apply the changes by reloading UFW and restarting Fail2ban:
sudo ufw reload
sudo systemctl restart fail2ban
This ensures both your new firewall chains and Fail2ban configurations are actively enforced.
Monitor and Test Fail2ban Protection
Check the status of your synflood
jail:
sudo fail2ban-client status synflood
This shows how many IPs are currently banned, total bans, and the list of blocked IPs.
Let's inspect the content of the custom chain we created:
sudo iptables -L fail2ban -v -n
You will notice two rules inside this chain:
- The first redirects traffic to a new chain called
f2b-synflood
, which Fail2ban automatically creates to manage and organize firewall rules. This ensures that if the same chain is used for another jail, the rules remain separate. - The second rule allows any traffic not handled by the
f2b-synflood
chain to proceed.
To view the block rules that Fail2ban has added, examine the contents of the f2b-synflood
chain using the command:
sudo iptables -L f2b-synflood -v -n
Now, if I initiate an attack using hping3
, Fail2ban will detect the attack and add a rule to block my IP from accessing all ports:
Chain f2b-synflood (1 references)
pkts bytes target prot opt in out source destination
1657K 66M REJECT 0 -- * * 192.168.64.7 0.0.0.0/0 reject-with icmp-port-unreachable
445K 18M RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/0
And indeed, Fail2ban successfully caught me.
If I want to unblock my IP, I can simply use the command:
sudo fail2ban-client set synflood unbanip 192.168.64.7
It's simpler compared to using the recent
module.
You can also check the IPs that Fail2ban has identified in the log file but has not yet blocked (because they are still under the limit) by using the sudo grep synflood /var/log/fail2ban.log
command.
Key Consideration & Recommended Safeguards
Before wrapping up, I'd like to highlight an important point I mentioned earlier in this guide.
Our current setup – which limits each IP to 10 SYN packets per second with an initial burst of 20 – works well for blocking small to medium-sized SYN flood attacks coming from a few sources.
Since our rate-limiting rules apply per IP address, such an attack might bypass the firewall entirely – because each IP appears "well-behaved" individually. The total volume, however, could still overwhelm your server.
I haven’t tested this large-scale scenario myself, so I can't say exactly how it would behave – but it's definitely something to consider in your threat model.
What You Can Do:
- Use a server provider with built-in DDoS protection: Providers like Hetzner block malicious traffic at the network level – before it hits your server.
- Enable rate limiting in NGINX: Use modules like
limit_req
andlimit_conn
to throttle request rates and concurrent connections per IP. - Use a CDN like Cloudflare: Cloudflare offers DDoS protection, WAF, and edge caching, filtering harmful traffic before it reaches your origin server.
Combining these external protections with your current UFW and Fail2ban setup creates a multi-layered defense strategy that can handle both targeted and large-scale attacks effectively.
Conclusion and Final Thoughts
In this guide, we walked through multiple approaches to protect your server against SYN flood attacks – from simple firewall rules using limit
, to more advanced setups using hashlimit
, recent
, and finally integrating Fail2ban for persistent, dynamic IP blocking.
While the process involved several steps and configurations, the end result is a much more secure server, capable of mitigating SYN flood attacks while still allowing legitimate traffic through.
💬 Found this guide helpful?
I'd love to hear about your experiences, questions, or ideas in the discussion section below. Your feedback not only helps improve future guides – it helps fellow admins on their own journey.
Prefer a more direct conversation? Feel free to contact me anytime.
Discussion