Published on 2025-11-19 by Jose Falanga
We already explored in this previous
post ways to harden against common threats and bots targeting our
public servers. We used the strategy of banning IPs if certain amount of
failed requests was met, in the range of the 4xx.
However, what if the attackers employ valid 2xx status
requests as the attack vector? We can totally harden our server to deal
with that too. We will setup four complementary layers
that each stop different types of attacks at different stages of the
network stack.
In the config files used here, there is various settings you can tune for your use cases. Take that into consideration if your services actually expect more traffic per IP than these defaults.
Each layer handles threats that the others cannot:
Together they provide strong L3, L4, and L7 DDoS protection. As usual, this can be expanded an you can even have an extra layer on top, like Cloudflare, but is a good introduction to these topics.

Improves TCP resilience, handles malformed traffic, and enables a modern congestion algorithm.
Threat addressed: Low-level floods and malformed packets attempting to overwhelm the network stack.
Edit:
sudo nano /etc/sysctl.confAdd:
net.ipv4.tcp_syncookies = 1
net.netfilter.nf_conntrack_max = 262144
net.ipv4.tcp_invalid_ratelimit = 500
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbrApply:
sudo sysctl -pImplements per-IP rate limiting for TCP handshakes and blocks UDP floods.
Threat addressed: High-volume L3/L4 attacks (SYN floods, UDP floods, connection-rate floods).
Edit:
sudo nano /etc/nftables.confThis is what the defaults looks like:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
}
chain forward {
type filter hook forward priority 0;
}
chain output {
type filter hook output priority 0;
}
}Inside the chain input block, add:
# SYN Flood Protection
tcp dport {80,443} flags syn meter ddos_syn { ip saddr limit rate 20/second burst 40 packets } accept
tcp dport {80,443} flags syn drop
# Drop UDP on web ports
udp dport {80,443} dropSo it looks like this:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
# SYN Flood Protection
tcp dport {80,443} flags syn meter ddos_syn { ip saddr limit rate 20/second burst 40 packets } accept
tcp dport {80,443} flags syn drop
# Drop UDP on web ports
udp dport {80,443} drop
}
chain forward {
type filter hook forward priority 0;
}
chain output {
type filter hook output priority 0;
}
}Apply:
sudo nft -f /etc/nftables.confBlocks clients generating excessive HTTP requests in a short window.
Threat addressed: L7 abuse such as GET/POST floods, aggressive scanning, scraping.
Edit:
sudo nano /etc/fail2ban/jail.d/http-get-flood.confContents:
[http-get-flood]
enabled = true
port = http,https
logpath = /var/log/caddy/access.log
filter = http-get-flood
maxretry = 120
findtime = 10
bantime = 3600Meaning: If an IP makes 120 requests in 10 seconds, it gets banned for 1 hour.
Edit:
sudo nano /etc/fail2ban/filter.d/http-get-flood.confContents:
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD) .*Restart:
sudo systemctl restart fail2banRemoves identifying headers, hides error pages, and adds local flood protection.
Threat addressed: L7 fingerprinting, reconnaissance, and excessive request bursts hitting Caddy directly.
Edit:
sudo nano CaddyfileDefine this template at the top:
(fingerprint_protection) {
header {
-Server
-X-Powered-By
-Via
-X-Response-Time-Ms
-Transfermode.dlna.org
-Realtimeinfo.dlna.org
}
handle_errors {
respond "An error occurred." 500
}
}Import it on every block you want protected:
example.thergic.ar {
import fingerprint_protection
reverse_proxy localhost:1234
...
}Reload:
sudo systemctl restart caddyThis guide is meant for learning how hardening a self-hosted VM or home lab works. These layers will protect you from bottom-feeders attacking your exposed homebrew services.
Actual commercial services available to the general public, with a lot more traffic will be far more exposed and I recommend you using another layer on top of all this to prevent other DDoS attacks. While all these layers might mitigate the effects of most threats, some large scale attacks using botnets with a lot of IPs will still hurt you and make your services unavailable or use up your resources.
Be careful on what you are exposing to the public. Have a good backup policy. Thread safely. Good luck!