Security 8 min read

cPanel Server Under DDoS: Find the Vhost and Block It

Load spikes, the box crawls, and every domain on the server looks equally guilty. Here is how to find the one that is not, and stop it, without touching the others.

By BashPilot Team

Load average climbs past 30, WHM is slow to respond, and every site on the box feels the pain even though only one of them is actually the target. This is what a cPanel server DDoS attack usually looks like at the application layer: one vhost gets flooded with application-level requests, Apache or LiteSpeed spawns workers to handle them, and every other tenant on the server starves for the same CPU and memory. The fix has nothing to do with any individual site's code. It is entirely about finding which vhost is absorbing the traffic and cutting off the source before the whole box falls over.

Confirm it is one vhost, not the whole server

Start with load and see whether one process is dominating, then find which domain owns it. top sorted by CPU usually shows a stack of httpd or lsphp workers all at high CPU, which tells you the server is busy serving requests, not that anything is broken. What it will not tell you is which site those requests belong to.

quick load and connection check
uptime
ps aux --sort=-%cpu | head -15
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn

The ss count shows how many connections are in each TCP state. A large SYN_RECV or ESTABLISHED count is the first real signal.

The actual answer is in the per-domain access logs. On cPanel these live at /usr/local/apache/domlogs/<domain> or, on newer installs with the combined logs feature, under /etc/apache2/logs/domlogs/. Count requests per domain in the last few minutes across all of them and the flooded one is obvious, it will be an order of magnitude above the rest:

requests per domain in the last few minutes
for f in /usr/local/apache/domlogs/*; do
  n=$(tail -n 5000 "$f" 2>/dev/null | wc -l)
  echo "$n $f"
done | sort -rn | head -10

Find the source inside that vhost's log

Once you have the domain, the same domlog tells you whether this is a few IPs hitting hard or many IPs hitting lightly. That distinction decides which tool you reach for next.

top requesting IPs for the flooded domain, last 10000 lines
tail -n 10000 /usr/local/apache/domlogs/example.com | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

A handful of IPs each responsible for thousands of hits is the easy case, block them and load drops immediately. If instead you see hundreds of distinct IPs each only a few requests deep, that is a distributed low-and-slow pattern designed specifically to stay under per-IP rate limits. It is worth also checking which URL is being hit and what response codes it is getting, since a flood of requests all returning 500 means the application itself is struggling under the load in a way that compounds the problem:

which URL, and how the server responded
tail -n 10000 /usr/local/apache/domlogs/example.com | awk '{print $7, $9}' | sort | uniq -c | sort -rn | head -20

Block the source at the server

On a stock cPanel and WHM install, CSF (ConfigServer Security and Firewall) is the fastest way to drop traffic before it reaches Apache or LiteSpeed at all. Blocking here is cheaper than letting the web server process and reject the request, because the connection never gets that far.

block one IP immediately
csf -d 203.0.113.44 "layer 7 flood on example.com"

For a handful of offenders, script it from the list you already produced:

block the top 10 offending IPs in one pass
tail -n 10000 /usr/local/apache/domlogs/example.com \
  | awk '{print $1}' | sort | uniq -c | sort -rn | head -10 \
  | awk '{print $2}' \
  | xargs -I{} csf -d {} "layer 7 flood on example.com"

For the distributed low-and-slow pattern, individual blocks do not scale. Two things help more: mod_evasive, which rate limits by IP automatically and blocks anything that exceeds a request threshold within a short window, and CSF's own connection tracking (CT_LIMIT in /etc/csf/csf.conf), which caps simultaneous connections per IP regardless of how many distinct IPs are involved.

check whether mod_evasive is active
apache2ctl -M 2>/dev/null | grep evasive
ls /etc/apache2/mods-enabled/ | grep evasive

Confirm it actually worked

Blocking IPs and walking away is how the same attack comes back an hour later from a fresh range. Re-check load, re-check the connection count, and re-check that the domlog for the target vhost has actually gone quiet, not just that the commands you ran exited without an error:

re-check after blocking
uptime
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn
tail -n 200 /usr/local/apache/domlogs/example.com | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

If load has not dropped within a couple of minutes of blocking, the attack has likely shifted source IPs, which is common with rented botnets. Repeat the domlog count rather than assuming the first round of blocks was the end of it.

That whole sequence, find the vhost, count IPs, check the URL pattern, block, re-check, is exactly what an operator like BashPilot runs when asked to check a server for an active attack. Here is the same triage as one conversation, with the details changed to a generic example:

In BashPilot

See how BashPilot handles this

app.bashpilot.com
find ddos attack
DDoS assessment, last 60 minutes

The server is under active attack from multiple vectors at once. One IP is responsible for a sustained flood against a single vhost, a separate group of IPs is running a distributed brute force against wp-login.php across several domains, and a third, much larger group is making only two or three requests each against the site root, the classic low-and-slow pattern built to stay under per-IP rate limits.

  • Single-IP flood: one address responsible for several thousand requests to one path on bashpilot.com, all successful, consistent with a scraper or API abuser rather than a vulnerability scan.
  • Distributed brute force: over 40 distinct IPs hitting /wp-login.php across more than 50 domains on this server, several returning 302 redirects, worth checking for a successful login.
  • Low-and-slow: 242 distinct IPs each averaging about two requests to /, designed specifically to evade simple rate limiting.
MetricLast 60 min
Total requests33,856
Distinct IPs1,443
Error rate (4xx + 5xx)51.2%
5xx errors1,704
block the worst offender and check load again

This will block the single IP responsible for the flood against bashpilot.com and re-check server load once applied. Approve to continue.

bash
csf -d 203.0.113.44 "layer 7 flood on bashpilot.com"
Example output, not a screenshot of a specific customer's server.

Try this on one of your own servers.

Start free trial

What this does and does not fix

Everything above stops traffic once it has already reached your server's network interface. It does nothing about the pipe itself being saturated, which is a true volumetric attack and has to be absorbed upstream, at Cloudflare, your provider's DDoS scrubbing, or a CDN sitting in front of the box. If uptime shows normal load but the server is still unreachable, that is a bandwidth problem, not a vhost problem, and no amount of CSF blocking on the origin server will touch it. The triage in this article is specifically for the case where the server is reachable, but struggling, because it is doing real work processing a flood of application-level requests.

Why this is worth automating

Doing that correctly under pressure, at 2am, across a server hosting forty unrelated customer sites, is exactly where mistakes happen: blocking a legitimate IP, missing that the attack moved domains, or declaring victory before load has actually recovered. That is the part worth handing off, not the judgment about which sites matter most.

Share LinkedIn X
Questions

Frequently asked questions

How do I know if my cPanel server is under a DDoS attack or just getting legitimate traffic?

Check the per-domain request counts in domlogs. Legitimate traffic spikes are usually spread across many URLs on a site with a reasonable spread of user agents and referrers. An attack typically shows either a small number of IPs generating a disproportionate share of requests to one domain, or hundreds of IPs each making the same small number of requests to the same path, often with no user agent or an obviously fake one, and frequently with an unusually high error rate.

Will blocking IPs with CSF stop a volumetric DDoS attack?

No. CSF blocks at your server, which only helps once traffic has already used your bandwidth to arrive. A true volumetric attack saturates the network link itself, and no amount of server-side blocking touches that. CSF and mod_evasive are effective against layer 7 application floods, where the traffic looks like normal HTTP requests and the server has to do real work processing each one, which is a different problem from the pipe being full.

What is the difference between mod_evasive and CSF for this?

CSF blocks at the firewall level using rules you set, either manually per IP or via its connection limits, and it protects every service on the server, not just the web server. mod_evasive works inside Apache itself, watching for request rate patterns to the same page from the same IP and temporarily blocking at the application layer. They are complementary: CSF is the blunt, fast, manual tool, mod_evasive is the automatic, ongoing protection that catches new attacks without you noticing at 2am.

Why does one attacked site slow down every other site on the server?

Shared cPanel servers share CPU, memory, and worker process limits across every domain. When Apache or LiteSpeed spawns workers to handle a flood of requests to one vhost, those workers consume resources that every other tenant on the box was also relying on, so load climbs for everyone even though only one domain is the actual target. This is also why finding and blocking the true source matters more than restarting services, which does not remove the underlying flood.

Put your servers on autopilot.

Connect a server in about a minute. The first week is on us.