You detect a DDoS attack by watching for several metrics jumping at once with no business reason: requests per second, half-open TCP connections, inbound bandwidth and a full connection-tracking table. Attack traffic usually hits the same few paths, sends no referrer and never converts. nginx access logs, ss, iftop and your CDN's analytics show all of this within minutes.
Most site owners do not find out about an attack from a dashboard. They find out from a customer email saying checkout has been spinning for ten minutes. By then the question is not whether something is wrong but what: a broken release, a database lock, or someone deliberately flooding the server. Being able to detect a DDoS attack quickly is mostly about answering that question with data instead of guesses.
Signs of a DDoS attack: what actually changes
A fault usually moves one metric. An attack moves several at once, abruptly, and without an obvious trigger such as a newsletter or a press mention. Use the table as a starting point and calibrate it against your own baseline.
| Metric | Where to look | Suspicious when |
|---|---|---|
| Requests per second | access log, CDN analytics | many times your usual daily peak |
| Inbound bandwidth | iftop, provider graphs | link near capacity, outbound flat |
| Half-open TCP connections | ss -tan state syn-recv | hundreds or thousands |
| Conntrack entries | /proc/sys/net/netfilter/nf_conntrack_count | close to nf_conntrack_max |
| Error rate | access log (499, 502, 503, 504) | climbing sharply |
| Orders or sign-ups | your app’s backend | traffic up, conversions at zero |
That last row is the cheapest test you have. Real people buy things. Botnets do not.
First commands when the server crawls
Spend two minutes looking before you start restarting services. These run on any current Linux distribution; iftop may need installing.
ss -s
ss -tan state syn-recv | wc -l
cat /proc/sys/net/netfilter/nf_conntrack_count /proc/sys/net/netfilter/nf_conntrack_max
Typical ss -s output during a connection flood looks something like this:
Total: 52418
TCP: 51960 (estab 1033, closed 187, orphaned 4, timewait 176)
Transport Total IP IPv6
RAW 0 0 0
UDP 11 7 4
TCP 51773 51751 22
INET 51784 51758 26
FRAG 0 0 0
A TCP total far above the established count means connections are stuck in other states. The second command counts the half-open ones. On a healthy web server that number is normally small; thousands point to a SYN flood. Check sysctl net.ipv4.tcp_syncookies while you are there. A value of 1 means SYN cookies are on, which lets the kernel keep answering without filling its backlog.
For bandwidth, run iftop -i eth0 -nNP. The flags turn off DNS and port name lookups and show port numbers. Heavy inbound UDP from source port 53 or 123, when your machine never sends DNS or NTP queries of that volume, is reflected traffic. The mechanism is covered in DNS amplification attacks.
Finally, dmesg -T | tail -50. If you see nf_conntrack: table full, dropping packet, the kernel is already discarding packets from everyone, legitimate visitors included.
Reading nginx access logs during an attack
For HTTP floods the access log tells you most of what you need. These one-liners assume the default combined format. On a busy server, pipe in only the last chunk with tail -n 100000 first.
Requests per minute, to see when it started:
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2-3 | sort | uniq -c | tail -30
Top client IPs:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Top paths and user agents:
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Patterns that usually mean a flood rather than an audience:
- one expensive endpoint (search, login, cart, an API route) dominating the count
- random query strings such as
/?x7q2p, designed to miss the cache - a handful of identical user-agent strings spread over thousands of IPs
- an empty referrer on almost every request
- lots of 499s, meaning clients hang up before the response arrives
If the load is spread across many pages and the referrers point at a news site or a viral post, you are probably looking at real people. The article on telling a DDoS or traffic spike apart goes through that decision in detail.
Behind a CDN, every request in the log comes from the CDN’s edge. Set set_real_ip_from for its published ranges and real_ip_header to its client-IP header (CF-Connecting-IP for Cloudflare) before you block anything.
Using CDN analytics for DDoS monitoring
If the domain is proxied through Cloudflare, the Security section lists mitigated events and the analytics views break traffic down by country, ASN, path and user agent. Two views earn their keep:
- Traffic by ASN. Real users arrive through consumer ISPs and mobile carriers. Floods tend to cluster in hosting and cloud networks.
- Cached versus origin requests. If edge traffic is flat but origin requests are climbing, someone is targeting content the cache cannot serve.
Settings worth adjusting ahead of time are in the guide to Cloudflare DDoS protection.
Which layer is under attack
Mitigation depends on where the pressure is. A rough map:
| Symptom | Likely layer | Who can filter it |
|---|---|---|
| Link saturated, CPU idle | network, volumetric | host or upstream provider |
| SYN_RECV high, conntrack full | protocol | kernel, firewall, provider |
| CPU and app workers maxed, link fine | application (HTTP) | web server, WAF, CDN |
| Site fine by IP, name lookups failing | DNS | DNS provider |
You cannot fix a saturated link from the server behind it. The packets have already clogged the pipe. A fuller breakdown is in types of DDoS attacks.
Evidence checklist while it is happening
Whatever you collect now will be needed by your provider, your insurer and possibly the police.
- start time, with time zone
- copies of access and error logs before rotation removes them
ss -sandiftopoutput saved to a file- a CDN analytics export for the period
- any ransom or threat emails, with full headers
- ticket numbers from your host and DNS provider
Once you know what you are dealing with, move on to the DDoS response steps.
Don’t forget the name servers
Some attacks skip the web server entirely and go after DNS. The origin is healthy; nobody can resolve it. Add an external check such as dig +short example.com A @ns1.your-provider.net every few minutes so you notice that case too.
How much you can do about it depends on your registrar. At betadomain you control name server management yourself, can switch on DNSSEC, and can script changes through the domain API for developers if you ever need to fail over to another DNS provider. Renewal stays at the price you registered at, which you can confirm on the price list. Set up the DNS check today, before you need it.
Found your domain? You can check and register it directly at compare domain prices – yearly price from €2.99.
FAQ
How do I know if my website is being DDoSed or just slow?
Slowness from a bug or a bad deploy usually shows up in one place, such as database time or one PHP worker pool. An attack moves several numbers together: connections, bandwidth and requests all climb while sales or sign-ups stay flat. If nothing changed on your side and traffic jumped tenfold, assume an attack until proven otherwise.
Can a DDoS attack be detected before it takes the site down?
Often, yes. Alerts on requests per second, SYN_RECV counts and origin bandwidth set at two or three times your normal peak usually fire minutes before users notice. That window is enough to switch on stricter CDN rules or call your provider.
Why does my access log only show CDN addresses?
The CDN opens the connection to your origin, so its IP is what nginx sees. Configure the realip module with set_real_ip_from for the CDN's published ranges and real_ip_header for its client IP header. Until you do, any per-IP analysis or blocking targets the CDN itself.
Does my hosting provider detect DDoS attacks for me?
Many providers watch their network for large volumetric floods and will null-route or scrub the traffic. They rarely look at HTTP request patterns on your site. Application-layer floods are yours to spot.