Quick answer
You are probably being DDoSed if your site or server slows or fails while traffic arrives far faster than any marketing could deliver, from many sources or one odd uniform pattern, and it will not settle. Before acting, rule out the three things that look identical: a genuine traffic spike, a crawler storm, and an origin failure that is nothing to do with traffic. A handful of read-only commands on the server separate them in minutes.
01How can I tell if I am being DDoSed?
A DDoS attack shows up as a sudden, sustained flood: the site slows down or returns errors, resource use climbs, and the traffic arrives faster and from more sources than any legitimate event would produce. On their own none of those is proof. The signal is several of them at once, appearing far too quickly, and refusing to settle after a few minutes.
The trap is that three ordinary problems look the same from the outside. A genuine traffic spike (a product launch, a news mention), a crawler or bot storm, and an origin failure that has nothing to do with traffic all present as 'the site is down'. Acting on the wrong one wastes the time that matters most, so the first job is to tell them apart, not to start blocking addresses.
The rest of this page is a symptom-to-cause table, the read-only commands that confirm each cause, what the traffic and logs look like in each case, and short sections for game servers and home connections. If you have already confirmed an attack, move on to stopping a DDoS attack or the under-attack checklist.
02Symptom, cause, test: the decision table
Start from what you can see, then run the test in the third column before concluding anything. The last column names who generated the error you are seeing, because that decides where the fix has to happen: in the visitor's browser, at the CDN or proxy in front, or at your own origin.
| Symptom | Likely cause | Test that confirms it | Who generates it |
|---|---|---|---|
| Uplink saturated, server almost idle | Volumetric flood never reaching the application | Interface byte counters climbing far above normal | Upstream network / CDN |
| Connections time out (ERR_CONNECTION_TIMED_OUT), traffic looks light | Protocol attack or connection exhaustion | High syn-recv count, SYN-cookie counter rising | Origin host |
| CPU pinned, request rate only moderate | Application-layer flood hitting something expensive | Access log shows one route or query hammered | Origin application |
| Many sources, uniform user agent, no referrer | HTTP flood | Log one-liners: one UA, empty referrers, assets not fetched | Origin / CDN edge |
| Traffic normal, one feature errors | A bug, not an attack | Error is in one code path; overall rate is flat | Origin application |
| High traffic from real referrers, assets load | Genuine traffic spike | Varied user agents, real referrers, ramps over minutes | Legitimate visitors |
| One user agent or ASN, robotic paths | Crawler or scraper storm | Access log: single UA/network, sequential paths | Bots |
| 5xx but traffic is flat | Origin failure unrelated to load | A 503 from your own app, nothing in the SYN or byte counters | Origin application |
03Read-only commands that separate the four causes
These commands only read kernel and log state, so they are safe to run on a production server mid-incident. Work from the network inwards: first ask whether the link is full, then whether connections are half-open, then what the application is actually being asked for.
If the interface counters show the link saturated, nothing you do on the server helps, because the packets are consuming the uplink before the machine sees them; that is an upstream problem. If the link is fine but the half-open count and SYN-cookie counter are climbing, it is a SYN flood reaching the host. If both are fine but the CPU is pinned, read the access log, because it is an HTTP flood or a genuine spike.
# Is the link full? Read the interface counters twice, a second apart (interface names: ip -br link).
S=/sys/class/net/eth0/statistics
B1=$(cat $S/rx_bytes); P1=$(cat $S/rx_packets); sleep 1
B2=$(cat $S/rx_bytes); P2=$(cat $S/rx_packets)
echo "$(( (B2-B1)*8/1000000 )) Mbit/s in, $(( P2-P1 )) packets/s in"
# Half-open TCP connections, and the kernel's SYN-cookie / backlog counters.
ss -Htn state syn-recv | wc -l
nstat -az TcpExtSyncookiesSent TcpExtListenOverflows TcpExtListenDrops
# Which client addresses hold the most established connections to port 443?
ss -Htn state established '( sport = :443 )' \
| awk '{ sub(/:[0-9]+$/, "", $4); print $4 }' | sort | uniq -c | sort -rn | head
# Who is sending SYNs, by source address, out of 5,000 packets?
tcpdump -nn -q -c 5000 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn' \
| awk '{ sub(/\.[0-9]+$/, "", $3); print $3 }' | sort | uniq -c | sort -rn | head
# Bulk unsolicited UDP from source ports 53, 123, 389, 1900 or 11211 suggests reflection.
tcpdump -nn -q -c 5000 'udp and (src port 53 or src port 123 or src port 389 or src port 1900 or src port 11211)'04What a flood looks like in the access log
An application-layer flood leaves a clear fingerprint in the web server log, and these one-liners surface it. Real visitors fetch a page and then its assets, carry varied user agents, and usually arrive with a referrer. A flood tends to request the same route or random query strings, shares one user agent, carries no referrer, and never pulls the CSS or images.
- Genuine spike: varied user agents, real referrers, assets load, and the rate ramps over minutes rather than appearing as a wall
- Crawler storm: one user agent or one network, sequential or pattern-based paths, and often polite spacing
- HTTP flood: one user agent, empty referrers, the same route or random query strings, and assets never fetched
- Origin failure: the log rate is flat but responses are 5xx, which points at the application, not at traffic
L=/var/log/nginx/access.log
# Requests per minute: a wall, not a ramp, is the tell.
awk '{ print substr($4, 2, 17) }' $L | uniq -c | tail -20
# Busiest single seconds.
awk '{ print substr($4, 2, 20) }' $L | sort | uniq -c | sort -rn | head
# Top client addresses, and most-requested paths with the query string stripped.
awk '{ print $1 }' $L | sort | uniq -c | sort -rn | head
awk '{ split($7, p, "?"); print p[1] }' $L | sort | uniq -c | sort -rn | head
# One user agent dominating, and the share of requests with no referrer.
awk -F'"' '{ print $6 }' $L | sort | uniq -c | sort -rn | head -3
awk -F'"' '{ if ($4 == "-") e++ } END { printf "%.0f%% had no referrer\n", 100*e/NR }' $L
# Share of requests that were static assets: near zero is suspicious for a browser flood.
awk '{ if ($7 ~ /\.(css|js|png|jpe?g|webp|svg|woff2?)(\?|$)/) a++ }
END { printf "%.0f%% were assets\n", 100*a/NR }' $L05DDoS, traffic spike or lag: which is it?
A real traffic spike is good news arriving fast: it comes from identifiable referrers, the visitors behave like people (page, then assets, then another page), and the user-agent mix looks human. Verifiable crawlers such as a search engine's identify themselves and can be confirmed by reverse lookup, covered under web crawlers, and a storm of AI crawlers can be limited as described in how to block AI crawlers. A flood does none of this and does not relent.
'Lag' is the word players and users reach for, but it hides two different problems. If the uplink is saturated or the host is dropping packets, the latency is an attack symptom. If the link and the host are healthy and only one slow database query or external call is to blame, it is an application problem that scaling the server will not fix. The commands above tell you which, in that order: link, then host, then application.
When in doubt, compare against your own baseline. The useful question is not 'is traffic high' but 'is it higher, faster and less human than anything legitimate has ever produced here', and the log one-liners answer it.
06Signs of a DDoS on a game server
Game servers are attacked differently from websites, so the signs differ too. Most game traffic is UDP, and a flood often takes the form of a server-query flood (the small status requests that server browsers and launchers send), or a raw UDP flood aimed at the game port itself. Players see rubber-banding, timeouts and mass disconnects while the box's CPU may look fine, because the damage is in the packet rate and the uplink, not the application.
The confirming checks are the same network ones as above: watch the interface packet-per-second counter, and capture a few thousand UDP packets to see whether they come from many spoofed sources or are all the same server query. A website can be moved behind a proxy with a DNS change, but a game server's real address is published in launchers and server lists, so the route back online is different. See DDoS protection for game servers and, once confirmed, how to stop the attack.
07Is my home internet connection being DDoSed?
Possibly, if every device in the household loses its connection at the same moment, the router's traffic graph shows inbound traffic pinned at the line's maximum, and it keeps happening during the same game or stream. A home line has a fixed capacity, so a flood far smaller than anything aimed at a data centre is enough to fill it, and nothing on your PC or router can filter traffic that has already used up the line.
A drop that affects only one device, or slowness that comes and goes with the time of day, is more likely Wi-Fi, a congested provider or a faulty router. The party that can confirm a flood is your internet provider, because it sees the traffic before it reaches your line. Ask it to check, and ask whether it can give you a new public IP address; that only helps for as long as the new address is not shared again.
Questions
Frequently asked questions
Sources
Sources and verification
The ss and nstat (iproute2 6.1), tcpdump (4.99.3) and GNU awk 5.2 one-liners were run against synthetic connections, packets and a synthetic combined-format access log in isolated network namespaces on Debian 12, October 2026; the tcpdump filters were compiled with tcpdump -d.
Last updated