Every DDoS post-mortem we have reviewed in the last two years shares a common thread: the server that went down was not inherently weak. It was misconfigured in a way that amplified the attacker's leverage. A 1 Gbps attack should not take down a 10 Gbps-connected server — but it routinely does when the kernel, web server, or network stack is left in its default state.
This is not a theoretical list. These are the ten misconfigurations we encounter most frequently when onboarding new Flowtriq nodes. Fix all ten and you will survive attacks that would have previously taken you offline.
Open DNS Resolvers
An open DNS resolver accepts recursive queries from any IP address on the internet. Attackers use your server to amplify DNS reflection attacks: they send a small query with a spoofed source IP, and your resolver sends a much larger response to the victim. The amplification factor for DNS is typically 28x to 54x, meaning a 1 Mbps query stream becomes a 54 Mbps attack — sourced from your IP.
Test whether your server is an open resolver:
# From an external machine, query your server's IP for a large record dig +short ANY google.com @YOUR_SERVER_IP # If you get a full answer, your resolver is open. # You should see "connection timed out" or "REFUSED" instead.
If you are running BIND, restrict recursion to trusted networks. If you are not intentionally running a DNS server, block port 53 entirely:
# BIND: restrict recursion in named.conf.options
options {
recursion yes;
allow-recursion { 127.0.0.1; 10.0.0.0/8; 172.16.0.0/12; };
allow-query-cache { 127.0.0.1; 10.0.0.0/8; 172.16.0.0/12; };
};
# Or block DNS entirely if you don't need it
iptables -A INPUT -p udp --dport 53 -j DROP
iptables -A INPUT -p tcp --dport 53 -j DROP
Open resolvers are catalogued by scanning projects like the Open Resolver Project. If yours is listed, expect it to be exploited within days. Closing port 53 to external traffic is the single highest-impact change on this list.
No SYN Cookie Protection
SYN cookies allow the kernel to handle SYN floods without allocating memory for half-open connections. When tcp_syncookies is disabled, a modest SYN flood (as low as 50,000 PPS) can exhaust the SYN backlog and make your server unreachable — even though your bandwidth is nowhere near saturated.
Check and fix:
# Check current state sysctl net.ipv4.tcp_syncookies # If output is: net.ipv4.tcp_syncookies = 0 — you are vulnerable # Enable SYN cookies immediately sysctl -w net.ipv4.tcp_syncookies=1 # Make it permanent echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/99-ddos-hardening.conf sysctl -p /etc/sysctl.d/99-ddos-hardening.conf
With SYN cookies enabled, the kernel encodes the connection state in the sequence number of the SYN-ACK response. No memory is consumed until the three-way handshake completes. The only downside is that TCP options (window scaling, SACK) are limited during the cookie phase — a negligible tradeoff for staying online.
Unlimited Connection Tracking
Linux's conntrack module maintains a table of every active connection so that stateful firewall rules work. The default nf_conntrack_max is typically 65,536. Under a DDoS, the table fills up in seconds. Once full, every new connection is dropped — including legitimate traffic. You will see nf_conntrack: table full, dropping packet in dmesg and wonder why your server is unreachable despite having plenty of bandwidth.
# Check current conntrack table size and usage sysctl net.netfilter.nf_conntrack_max cat /proc/sys/net/netfilter/nf_conntrack_count # If count is anywhere near max, you are at risk # Increase the table size (use ~256 bytes per entry as a memory guide) # 1M entries ≈ 256 MB RAM — reasonable for a server with 8+ GB sysctl -w net.netfilter.nf_conntrack_max=1048576 # Reduce timeout for established connections (default 432000 = 5 days!) sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=1800 sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30 # Make permanent cat >> /etc/sysctl.d/99-ddos-hardening.conf << 'EOF' net.netfilter.nf_conntrack_max = 1048576 net.netfilter.nf_conntrack_tcp_timeout_established = 1800 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30 EOF
If you do not use stateful firewall rules (no
-m stateor-m conntrackin your iptables), you can disable conntrack entirely withiptables -t raw -A PREROUTING -j NOTRACK. This eliminates the table-full problem completely.
No Rate Limiting on Nginx/Apache
Without rate limiting, a single attacker can open thousands of connections per second and exhaust your web server's worker pool. Legitimate users see 502 or 503 errors while the attacker's requests are served just as happily as anyone else's. Application-layer DDoS (Layer 7) specifically exploits this — HTTP floods are designed to look like normal traffic, so you need rate limits to cap the damage any single source can do.
Nginx rate limiting:
# In nginx.conf http block
http {
# Define a rate limit zone: 10 requests/second per IP
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
# Stricter zone for login/API endpoints
limit_req_zone $binary_remote_addr zone=auth:10m rate=2r/s;
server {
# Apply global rate limit with a burst allowance
limit_req zone=global burst=20 nodelay;
limit_req_status 429;
location /login {
limit_req zone=auth burst=5 nodelay;
# ... proxy_pass or fastcgi_pass
}
}
}
Apache rate limiting with mod_ratelimit and mod_evasive:
# Enable mod_evasive for DDoS protection # /etc/apache2/mods-available/evasive.confDOSHashTableSize 3097 DOSPageCount 5 DOSSiteCount 50 DOSPageInterval 1 DOSSiteInterval 1 DOSBlockingPeriod 60 DOSEmailNotify [email protected] # Or use mod_ratelimit to throttle bandwidth per connectionSetOutputFilter RATE_LIMIT SetEnv rate-limit 400
Exposed Memcached on UDP
Memcached listening on UDP port 11211 is arguably the most dangerous amplification vector ever discovered. The amplification factor exceeds 51,000x. A single 203-byte request can generate a 100 MB response. In February 2018, this was used to launch a 1.7 Tbps attack against GitHub — the largest DDoS attack recorded at that time.
If Memcached is listening on UDP and reachable from the internet, you are an amplification weapon:
# Check if Memcached is listening on UDP
ss -ulnp | grep 11211
# If you see output like this, you are exposed:
# UNCONN 0 0 0.0.0.0:11211 0.0.0.0:* users:(("memcached",pid=1234,fd=26))
# Fix 1: Disable UDP in Memcached config (/etc/memcached.conf)
-U 0
-l 127.0.0.1
# Fix 2: Restart Memcached
systemctl restart memcached
# Fix 3: Firewall as a safety net
iptables -A INPUT -p udp --dport 11211 -j DROP
iptables -A INPUT -p tcp --dport 11211 ! -s 127.0.0.1 -j DROP
After fixing, verify from an external host:
# This should time out or be refused echo "stats" | nc -u -w1 YOUR_SERVER_IP 11211
How many of these apply to your servers?
Flowtriq monitors your nodes in real time, detects DDoS attacks in under 2 seconds, and alerts you before damage spreads. 7-day free trial, no credit card required.
Start Free Trial →Default SSH on Port 22 with Password Auth
SSH on port 22 with password authentication enabled is the most brute-forced service on the internet. Automated scanners (including Mirai variants) attempt thousands of credential combinations per hour against every publicly reachable port 22. Even if they do not break in, the connection overhead from brute-force attempts consumes kernel resources — SYN backlog slots, conntrack entries, and sshd fork/exec cycles — that add up during a coordinated attack.
# Harden SSH in /etc/ssh/sshd_config Port 2222 # Move off port 22 PasswordAuthentication no # Key-only authentication PermitRootLogin no # No root login MaxAuthTries 3 # Limit auth attempts per connection LoginGraceTime 20 # 20 seconds to authenticate MaxStartups 10:30:60 # Rate-limit unauthenticated connections # Restart sshd systemctl restart sshd # Don't forget to update your firewall iptables -A INPUT -p tcp --dport 2222 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP
The MaxStartups 10:30:60 directive is particularly important for DDoS resilience. It means: after 10 unauthenticated connections, start dropping 30% of new attempts, and drop 100% after 60. This prevents SSH from becoming a resource exhaustion vector.
Missing Reverse Path Filtering
Reverse path filtering (rp_filter) causes the kernel to drop incoming packets whose source address would not be routable back through the same interface. When rp_filter=0, your server accepts packets with spoofed source IPs. This is the fundamental enabler of all reflection and amplification attacks — the attacker spoofs the victim's IP as the source, and your server sends the amplified response to the victim.
# Check current rp_filter setting sysctl net.ipv4.conf.all.rp_filter sysctl net.ipv4.conf.default.rp_filter # If either is 0, enable strict reverse path filtering sysctl -w net.ipv4.conf.all.rp_filter=1 sysctl -w net.ipv4.conf.default.rp_filter=1 # Make permanent cat >> /etc/sysctl.d/99-ddos-hardening.conf << 'EOF' net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 EOF
There is also a "loose" mode (rp_filter=2) that only checks whether the source IP is reachable via any interface, not necessarily the one it arrived on. Use strict mode (=1) unless you have asymmetric routing, in which case loose mode is the pragmatic choice. Either is better than disabled.
No Egress Filtering
Most administrators focus exclusively on inbound filtering. But if your server is compromised and used to launch DDoS attacks, egress filtering is what prevents you from becoming the attacker. Without it, a compromised process can send packets with any source IP to any destination — making your server a fully functional DDoS cannon.
# Allow outbound traffic only from your assigned IP(s) # Replace 203.0.113.5 with your server's actual IP iptables -A OUTPUT -s 203.0.113.5 -j ACCEPT iptables -A OUTPUT -s 127.0.0.0/8 -j ACCEPT iptables -A OUTPUT -j DROP # Block outbound to common amplification ports (as a safety net) iptables -A OUTPUT -p udp --dport 53 -m owner ! --uid-owner named -j DROP iptables -A OUTPUT -p udp --dport 123 -j DROP iptables -A OUTPUT -p udp --dport 11211 -j DROP iptables -A OUTPUT -p udp --dport 1900 -j DROP # Rate-limit outbound ICMP to prevent being used in ICMP floods iptables -A OUTPUT -p icmp --icmp-type echo-reply \ -m limit --limit 10/s --limit-burst 20 -j ACCEPT iptables -A OUTPUT -p icmp --icmp-type echo-reply -j DROP
BCP38 (RFC 2827) recommends that every network operator implement egress filtering. If your hosting provider does not filter spoofed traffic at the network edge, your server-level egress rules are the last line of defense. Implement them.
Unbounded Worker Processes
Setting MaxRequestWorkers (Apache) or worker_connections (Nginx) too high sounds like a good idea — more capacity, right? Wrong. When an HTTP flood hits, every one of those workers gets consumed by attack traffic. If you have 10,000 workers and an attacker sends 10,000 slow connections, you have zero capacity left for real users. Worse, each worker consumes memory, and your server starts swapping, which is the beginning of a full system freeze.
The goal is to set limits that match your server's actual capacity, not the theoretical maximum:
# Nginx: calculate based on available RAM and expected memory per connection
# /etc/nginx/nginx.conf
worker_processes auto; # Match CPU cores
events {
worker_connections 2048; # Per worker — not "as many as possible"
multi_accept on;
}
http {
# Limit total active connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 100;
# Set timeouts aggressively to free up workers
keepalive_timeout 15;
client_body_timeout 10;
client_header_timeout 10;
send_timeout 10;
}
# Apache: /etc/apache2/mods-available/mpm_prefork.conf
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 256 # Not 10000
MaxConnectionsPerChild 1000 # Recycle workers to prevent memory leaks
ServerLimit 256
The limit_conn directive in Nginx is equally important. Without it, a single IP can consume all of your worker_connections. With limit_conn addr 100, one attacker can consume at most 100 connections — annoying but not fatal.
No Monitoring or Alerting
This is the misconfiguration that makes all the others worse. You can have every kernel parameter tuned perfectly, and an attack will still eventually find a way through — a new vector, a traffic volume that exceeds your capacity, or a multi-vector attack that hits from several angles at once. Without monitoring, you discover the attack when customers start complaining. By then, the damage is done.
The minimum monitoring stack for DDoS resilience requires three things:
- Per-second traffic metrics. Not per-minute. DDoS attacks spike in sub-second intervals. If your monitoring polls every 60 seconds, a 45-second attack is invisible in your data.
- Automatic anomaly detection. Static thresholds break. Traffic patterns change with time of day, day of week, and seasonality. You need dynamic baselines that adapt — and fire alerts when traffic deviates from the learned pattern, not when it crosses an arbitrary number you picked six months ago.
- Multi-channel alerting. An email alert during a DDoS attack is useless if the attack is taking down your mail server. You need alerts via Discord, Slack, PagerDuty, SMS, and webhook simultaneously.
This is exactly the problem Flowtriq was built to solve. The agent runs on your node, reads /proc/net/dev every second, detects anomalies against a rolling baseline, classifies the attack type, captures a PCAP for forensic evidence, and fires alerts to every channel you have configured — all within 2 seconds of attack onset.
# What you are replacing with Flowtriq: # - Manual tcpdump sessions you remember to start after the attack begins # - Cacti/Munin graphs you check the morning after # - A Slack message from a customer saying "site is down" # What Flowtriq provides: # - Per-second PPS and BPS monitoring with dynamic baselines # - Automatic attack classification (SYN flood, UDP flood, DNS amp, etc.) # - On-demand PCAP capture during incidents # - Alerts via email, Discord, Slack, PagerDuty, OpsGenie, SMS, webhook # - $9.99/node/month, 7-day free trial
The compound effect: Each misconfiguration on this list reduces your effective DDoS resilience. But the damage is not additive — it is multiplicative. An open resolver (item 1) with no egress filtering (item 8) and no monitoring (item 10) means your server can be weaponized against others, you will not know it is happening, and you will not be able to prove you were not complicit when the abuse complaints arrive.
The Complete Hardening Checklist
Here is a single script that applies all nine kernel and firewall fixes from this article. Review it, adjust the IP addresses, and run it on every server you manage:
#!/bin/bash # DDoS hardening — apply all sysctl and firewall fixes # Review and adjust before running in production # --- Kernel parameters --- cat > /etc/sysctl.d/99-ddos-hardening.conf << 'SYSCTL' net.ipv4.tcp_syncookies = 1 net.netfilter.nf_conntrack_max = 1048576 net.netfilter.nf_conntrack_tcp_timeout_established = 1800 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 SYSCTL sysctl -p /etc/sysctl.d/99-ddos-hardening.conf # --- Firewall rules --- # Block open DNS resolver iptables -A INPUT -p udp --dport 53 -j DROP iptables -A INPUT -p tcp --dport 53 -j DROP # Block Memcached from external access iptables -A INPUT -p udp --dport 11211 -j DROP iptables -A INPUT -p tcp --dport 11211 ! -s 127.0.0.1 -j DROP # Egress: block common amplification ports iptables -A OUTPUT -p udp --dport 11211 -j DROP iptables -A OUTPUT -p udp --dport 1900 -j DROP echo "Hardening applied. Verify with: sysctl -a | grep -E 'syncookies|conntrack_max|rp_filter'"
Fix the configs. Then add the monitoring.
Flowtriq gives you per-second DDoS detection, automatic attack classification, PCAP forensics, and instant multi-channel alerts. $9.99/node/month.
Start your free 7-day trial →