You can harden a cPanel server in an afternoon, but most production environments undo half of it within a week. The problem isn't lack of tools—it's the gap between what security guides recommend and what survives contact with real hosting workloads, support tickets, and the developer who needs SFTP access right now.
I've seen servers locked down beautifully on day one, then slowly opened back up as exceptions pile on. This checklist reflects what actually holds up under production pressure and the anti-patterns that break hardening faster than an attack does.
SSH access patterns that matter
Disable root SSH login immediately. Edit /etc/ssh/sshd_config and set PermitRootLogin no. Every compromised host I've investigated had root SSH enabled, usually with password authentication still on.
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
Use key-based authentication exclusively. Passwords get brute-forced. The anti-pattern here is keeping password auth "just for emergencies"—that emergency becomes the vector.
Change the SSH port from 22 to something above 10000. Yes, it's security through obscurity. It also drops bot traffic by 95% and keeps your logs readable. In /etc/ssh/sshd_config, set Port 22022 or similar, then update your firewall rules before restarting SSH or you'll lock yourself out.
sudo firewall-cmd --permanent --add-port=22022/tcp
sudo firewall-cmd --reload
The trap: changing the port in sshd_config but forgetting to allow it in CSF or firewalld. I've responded to "server is down" tickets that were just this.
Firewall configuration that doesn't break services
Install ConfigServer Security & Firewall (CSF) through WHM if it's not already present. CSF integrates with cPanel's service detection and handles most edge cases better than raw iptables.
In /etc/csf/csf.conf, verify these settings:
TCP_IN = "20,21,22,25,53,80,110,143,443,465,587,993,995,2077,2078,2082,2083,2086,2087,2095,2096"
TCP_OUT = "20,21,22,25,37,43,53,80,110,113,443,587,873,993,995,2086,2087,2089,2703"
UDP_IN = "20,21,53"
UDP_OUT = "20,21,53,113,123"
Those are the standard cPanel ports. The anti-pattern is closing everything and opening only 80/443, then spending the next week troubleshooting why webmail, FTP, or cPanel itself won't connect.
Enable Login Failure Daemon (LFD) within CSF. Set LF_SSHD = "5" to block IPs after five failed SSH attempts in the LF_TRIGGER window (default 300 seconds). Set LF_TRIGGER_PERM = "1" to permanently block after repeated violations.
The second trap: setting thresholds too low. LF_SSHD = "2" will lock out legitimate users who mistype a password. Five is the floor for production.
cPanel-specific account isolation
Enable CageFS if you're running CloudLinux. It virtualizes the filesystem for each user, preventing them from seeing other accounts' processes or files. Without it, any compromised account can enumerate others via /home or ps aux.
In WHM, go to Security Center → CageFS and click Enable CageFS. Then initialize it:
/usr/sbin/cagefsctl --init
/usr/sbin/cagefsctl --enable-all
If you're not on CloudLinux, you're relying on standard Linux DAC permissions. That's fine for small shared hosting, risky for reseller accounts or high-density servers.
Turn on Compiler Access restrictions in WHM under Security Center → Compiler Access. Set it to Disabled or Jail mode. Compilers let attackers build custom exploits or privilege escalation tools directly on your server. The legitimate use case—developers compiling PHP extensions—happens rarely enough to handle manually.
ModSecurity rules that don't false-positive constantly
ModSecurity ships with cPanel but defaults to detection-only mode. In WHM, navigate to Security Center → ModSecurity Configuration and ensure it's enabled globally.
The OWASP Core Rule Set (CRS) is bundled. Don't enable all rules blindly—CRS in paranoia level 2+ will block legitimate WordPress admin actions, WooCommerce checkouts, and contact forms. Start at paranoia level 1.
Check /usr/local/apache/logs/modsec_audit.log weekly for patterns. If you see repeated blocks for a specific rule (e.g., 942100 for SQL injection) on a known-good application, whitelist that rule for the affected domain in WHM:
<IfModule mod_security2.c>
SecRuleRemoveById 942100
</IfModule>
The anti-pattern is disabling ModSecurity entirely after the first false positive. That's throwing away your WAF because tuning takes effort.
PHP and web application hardening
Disable dangerous PHP functions system-wide. Edit /usr/local/lib/php.ini (or the path shown by php --ini) and set:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
Most legitimate PHP applications don't need these. Malware does. If a customer's script breaks, handle it via .user.ini overrides in their specific document root rather than re-enabling globally.
Force SSL for cPanel and WHM logins. In WHM, go to Security Center → Manage cPanel & WHM TLS and enable Require SSL. Credentials over plain HTTP are a gift to anyone sniffing the network.
Set up automatic Let's Encrypt for all accounts via SSL/TLS → Manage AutoSSL. The anti-pattern here is issuing certificates manually and letting them expire. AutoSSL handles renewals and covers mail subdomains (mail.example.com, webmail.example.com).
Email server attack surface
Disable SMTP authentication on port 25 for untrusted IPs. Spammers scan for open relays and weak passwords on submission ports. In /etc/exim.conf (or via WHM's Exim Configuration Manager), ensure:
auth_advertise_hosts = ${if eq{$tls_cipher}{}{}{*}}
This allows SMTP auth only over TLS. For outbound from web apps, PHP scripts should use port 587 with authentication, not direct sendmail injection.
Enable SMTP Restrictions in WHM under Security Center → SMTP Restrictions. This prevents non-root users from connecting to remote SMTP servers on port 25, stopping backdoor scripts from sending spam directly. Legitimate mail still flows through Exim locally.
The trap: enabling this and not realizing a customer's custom application connects to an external SMTP relay. You'll get a ticket about "email broken" within hours. Check for outbound port 25 connections first:
netstat -ntp | grep :25
Set up SPF, DKIM, and DMARC records for all hosted domains. cPanel generates DKIM keys automatically in Email → Email Deliverability. DMARC policy should start at p=none with reporting, then move to p=quarantine after monitoring:
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]"
Backup integrity and off-server storage
cPanel's backup system is convenient but useless if backups live on the same disk. Configure remote backups to S3, SFTP, or another server in WHM → Backup → Backup Configuration.
Set retention to at least 7 daily backups. The anti-pattern is keeping only the most recent backup—if malware sits undetected for three days, your single backup is already poisoned.
Encrypt backups before sending them off-server. cPanel supports GPG encryption natively in the backup configuration. Generate a key pair, export the public key to the cPanel server, and configure encryption in the backup settings.
Test restores quarterly. I've seen production servers with two years of perfect daily backups that couldn't actually restore because of a misconfigured mount point. Verification is not optional.
Update discipline and the maintenance window
Enable automatic cPanel & WHM updates in WHM → Server Configuration → Update Preferences. Set the update tier to Release (stable) and allow automatic updates outside of your traffic peaks.
OS updates need manual scheduling because kernel updates require reboots. Run yum update or dnf update weekly during your maintenance window, then reboot:
sudo yum update -y && sudo reboot
The anti-pattern is never updating because "the server is stable." Stable and unpatched is just vulnerable with uptime.
Monitor for EasyApache updates separately. PHP, Apache, and module updates happen through EasyApache 4 in WHM, not yum. Check monthly and test in a staging environment if you have one.
Monitoring and intrusion detection
Install Imunify360 if your host supports it, or at minimum run ClamAV scans weekly. Imunify360 watches for real-time malware injection, proactive defense against zero-days, and integrates with cPanel's quarantine system.
For ClamAV on a budget:
sudo yum install clamav clamav-update
sudo freshclam
sudo clamscan -r --infected --remove /home
Set that as a weekly cron job. Reactively removing malware is worse than prevention, but it's better than leaving it.
Enable cPHulk Brute Force Protection in WHM under Security Center → cPHulk Brute Force Protection. Configure it to lock accounts after repeated failed login attempts to cPanel, webmail, FTP, and SSH.
The trap: setting cPHulk too aggressively and locking out your own IP. Whitelist your office or VPN IP range in the cPHulk interface first.
Watch /var/log/messages and /usr/local/cpanel/logs/access_log for anomalies. Sudden spikes in 404s, POST requests to XML-RPC endpoints, or access from unexpected countries are all signals worth investigating.
What still breaks with hardening
Developers will complain about disabled exec functions. Legacy PHP applications sometimes use shell_exec for image processing or system commands. Handle these with .user.ini exceptions rather than weakening global security.
FTP over port 21 will conflict with CSF if passive port ranges aren't opened. In /etc/pure-ftpd.conf or /etc/proftpd.conf, set a passive port range like 35000-35999, then add those ports to TCP_IN in CSF.
Cloudflare users forget to whitelist Cloudflare's IP ranges in CSF. If all traffic appears to come from Cloudflare IPs, your firewall will block legitimate users after rate-limiting triggers. Import Cloudflare's IP list into CSF's csf.allow.
Node.js or custom application servers on non-standard ports need explicit firewall rules. If you're running an app on port 3000 behind a reverse proxy, open 3000 only to localhost in CSF, not to the world.
How often should I audit these settings?
Monthly for firewall rules and user access. Quarterly for SSL certificates, backups, and software versions. After any compromise or major configuration change, re-audit everything.
Does CageFS slow down the server?
Negligible overhead on modern hardware. The CPU cost is low, and the memory footprint is only a few MB per user. Performance impact is far less than dealing with a cross-account breach.
Can I use fail2ban instead of CSF?
Yes, but CSF integrates better with cPanel's service stack and handles more edge cases out of the box. Fail2ban is fine for pure SSH protection on a VPS; CSF is better for full cPanel hosting.
Should I disable the root account entirely?
No. Use PermitRootLogin no in SSH and require sudo from a regular user. Some cPanel scripts and maintenance tasks expect root to exist.
The security tripwires to check first
When you inherit a cPanel server or audit an existing setup, check these immediately: root SSH login status, CSF installation and config, ModSecurity enabled or disabled, PHP disable_functions line, backup destination (local or remote), and cPHulk status. Those six items will tell you if the server was secured intentionally or just installed and forgotten.
Hardening isn't a one-time task. It's the habit of closing new holes as fast as they open and questioning every "temporary" exception that becomes permanent. The servers that stay secure are the ones where someone checks the logs, watches the firewall, and says no to shortcuts.
