To harden a self-hosted WHMCS server, work through WHMCS's own ten-step Enhancing Security list, then add the server layer it leaves to you: a default-deny firewall that exposes only ports 80 and 443 plus SSH from your own addresses, a maintained firewall tool, an IP-restricted admin directory, a read-only configuration.php and a database user without schema privileges for daily use.
This applies to self-hosted WHMCS only. WHMCS Cloud handles these steps for you.
What does the official WHMCS security checklist cover?
WHMCS's Enhancing Security page lists ten steps, and the last one, general server hardening, is the part this guide expands. The first nine, with what each involves:
| WHMCS step | What it involves |
|---|---|
| 1. Secure writeable directories | Move attachments, downloads and templates_c above the web root, set $templates_compiledir in configuration.php and switch the paths at Configuration > System Settings > Storage Settings |
| 2. Secure configuration.php | chmod 400 configuration.php, or 440 and then 444 if WHMCS fails to load. Loosen it only while you update the license key |
| 3. Move the crons directory | Put crons in a private directory above the web root |
| 4. Restrict the Admin Area | Allow only your staff's fixed IP addresses |
| 5. Rename the admin directory | Set $customadminpath and rename the folder to match |
| 6. Enable SSL | Serve WHMCS over HTTPS with a valid certificate |
| 7. Restrict database privileges | Remove the privileges WHMCS needs only for installs and updates |
| 8. Block the vendor directory | Apache is covered by the included .htaccess. Nginx needs its own rule |
| 9. Defend against clickjacking | Send a Content Security Policy frame-ancestors header |
WHMCS also recommends banning email addresses on your own company domain from creating client accounts, to blunt social engineering.
Which ports should a WHMCS server expose?
Only HTTP and HTTPS to the world, SSH to your own addresses, and nothing else unless a service on the same machine needs it. WHMCS itself listens on nothing but the web server.
| Port | Who needs it | Rule |
|---|---|---|
| 80, 443 | Clients, staff, gateway callbacks | Open to all |
| 22 (SSH) | You | Your IP addresses only, keys instead of passwords |
| 3306 (MySQL) | A remote database client, if any | Closed, or open to that one address |
| 2083, 2087, 2096 and others | Only when WHMCS shares a cPanel server | Restrict 2087 (WHM) to your addresses, and follow cPanel's port list for the rest |
cPanel's firewall documentation has the full port list, recommends opening only the services you use, and tells you to keep console access while you change rules. Outbound is a different matter. WHMCS calls its licensing server, payment gateways and registrars over HTTPS, reaches WHM on port 2087 to provision accounts, and may send mail through a relay. I leave outbound open unless I can list every destination, because a missed one breaks billing quietly.
If Cloudflare or another proxy sits in front, add its ranges under Trusted Proxies on the Security tab at Configuration > System Settings > General Settings. The Security tab documentation warns that otherwise every login and order appears to come from the proxy.
Which firewall tool should I use now that ConfigServer has closed?
On a cPanel server, CSF is still an option, because cPanel now maintains it. Way to the Web, the company behind ConfigServer, closed on 31 August 2025 and released CSF under the GPLv3. cPanel's support article says it began publishing a fork focused on critical security and stability fixes on 25 February 2026, and repointed eligible servers to its mirrors: CSF versions 14.00 to 15.00 with AUTO_UPDATES on. Earlier versions of this guide said not to install CSF at all. That advice is out of date.
| Option | Where it fits | What to know |
|---|---|---|
cPanel's CSF fork (cpanel-csf) | cPanel and WHM servers | cPanel gives yum install cpanel-csf (AlmaLinux, CloudLinux) or apt install cpanel-csf (Ubuntu) to move to it. The package fails when cPanel is not present. cPanel maintains it but does not support its configuration |
| firewalld or nftables | AlmaLinux, Rocky Linux, CloudLinux | cPanel recommends nftables on these systems. CSF and APF do not work alongside firewalld |
| iptables | Ubuntu | cPanel recommends it over ufw on Ubuntu servers |
| Provider network firewall | Any server | Filters before traffic reaches the machine. The best place to limit SSH and WHM |
Check what you run with cat /etc/csf/version.txt. A version at or below 15.00 that still points at download.configserver.com receives nothing. Also note cPanel's warning that once a third-party firewall is installed on an nftables system, rules in WHM's Host Access Control are ignored.
How do I set up firewalld without locking myself out?
Make every change in the runtime configuration first, test it from a second session, and only then save it. The firewall-cmd manual describes exactly this workflow for --runtime-to-permanent. If a runtime change locks you out, a reload from your provider's console restores the saved rules.
firewall-cmd --get-active-zones
firewall-cmd --add-service=http --add-service=https
firewall-cmd --add-rich-rule='rule family="ipv4" source address="203.0.113.10" service name="ssh" accept'
firewall-cmd --remove-service=ssh
# open a NEW SSH session from 203.0.113.10 and confirm it works, then:
firewall-cmd --runtime-to-permanent
firewall-cmd --list-all
Replace 203.0.113.10 with your own address and add a rule per address. If your office IP changes often, connect through a VPN with a fixed address instead of opening SSH to everyone. Then set PasswordAuthentication no in /etc/ssh/sshd_config once key login works.
How do I lock down the WHMCS admin area at the server level?
Restrict the admin directory by IP in the web server and rename it. WHMCS's Banned IP Addresses page gives the Apache 2.4 rule for a .htaccess file in the admin directory:
Require ip 203.0.113.10
Require ip 198.51.100.0/24
On nginx, use allow and deny all, and check that the rule also covers the location that hands .php files to PHP-FPM. A rule in a plain location /admin/ block does not apply to requests that a location ~ \.php$ block matches. Then rename the directory: add $customadminpath = "example"; to configuration.php and rename the folder to match. WHMCS warns that this breaks callback URLs that use the old path, and that every update needs the new admin files copied into your custom directory.
The WHMCS settings that back this up sit on the Security tab of General Settings:
- Failed Admin Login Ban Time. Three failed admin logins ban the IP for 15 minutes by default. Setting it to 0 turns the ban off.
- Whitelisted IPs. This only exempts addresses from that ban. It is not an allowlist.
- API IP Access Restriction lists the off-server addresses allowed to call the API, and Cron HTTP Access Restriction keeps
cron.phpblocked over HTTP, which is the default.
Turn on two-factor authentication at Configuration > System Settings > Two Factor Authentication. Time-based tokens fail when the server clock drifts, so keep time synchronization running on the server.
How do you force SSL (HTTPS) in WHMCS?
Install a valid certificate on the domain, set the WHMCS System URL to the exact https:// address, then redirect plain HTTP to HTTPS at the web server. WHMCS itself has no "force SSL" switch: the System URL decides the links it builds, and the redirect belongs to Apache, Nginx or your proxy.
- Certificate. Issue one for the hostname WHMCS runs on (AutoSSL or Let's Encrypt on most panels) and check the full chain loads without warnings.
- System URL. Go to Configuration > System Settings > General Settings, General tab, and enter the URL with https:// in WHMCS System URL, for example
https://www.example.com/whmcs/. The WHMCS documentation says the system URL must match exactly: starting it with http:// or dropping the www can cause errors. Older forum answers mention a separate SSL System URL field; current versions have the one setting. - Redirect HTTP to HTTPS. On Apache, add this to the .htaccess in the WHMCS folder (or the site root):
On Nginx, the port 80 server block returnsRewriteEngine On RewriteCond %{HTTPS} !=on RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]return 301 https://$host$request_uri;. - Behind Cloudflare or another proxy, use an SSL mode that talks HTTPS to your server (Full, strict) and let the proxy or the server redirect, not both in a way that loops. A proxy that connects to the origin over plain HTTP while the origin redirects to HTTPS is the usual cause of an endless redirect.
- Then test: an http:// link returns one 301 to the https:// page, the admin and client areas show no mixed-content warnings, file downloads work, and a test payment's gateway callback still reaches WHMCS.
Add HSTS only after every page and subdomain you use is on HTTPS, because browsers then refuse plain HTTP for the whole time the header sets.
Which file and database permissions does WHMCS need?
A read-only configuration.php, writable folders outside the web root, and a database user that loses its schema privileges between updates. Back up configuration.php before you touch it, because WHMCS says its contents cannot be recovered without a copy.
The system requirements list the privileges for installation: ALTER, CREATE, DROP, INDEX, LOCK TABLES, SELECT, INSERT, UPDATE and DELETE. For daily operation you can remove ALTER, CREATE, DROP and INDEX, which WHMCS needs only for installs, updates and activating or deactivating modules.
-- daily operation
REVOKE ALTER, CREATE, DROP, INDEX ON whmcs.* FROM 'whmcs'@'localhost';
-- before an update or a module activation
GRANT ALTER, CREATE, DROP, INDEX ON whmcs.* TO 'whmcs'@'localhost';
Keep MySQL or MariaDB listening on localhost. If the database must live on another server, configuration.php supports encrypted connections through the db_tls_* variables. On nginx, block the vendor directory, which WHMCS shows as location ^~ /vendor/ { deny all; return 403; }.
How do I check that the hardening worked?
Test from outside, the way an attacker would. Run these after the first pass and after every server move.
| Test | Result you need |
|---|---|
nmap against your own server from another network | Only the ports you meant to open |
| SSH from an address that is not on your list | Connection refused or timed out |
The old /admin/ URL | 404, not a login page |
| The new admin URL from an unlisted address | 403 |
/vendor/ and /crons/cron.php over HTTP | 403 or 404 |
/.git/HEAD | 403 or 404. See my exposed .git folder guide |
| A staff login | A two-factor prompt |
What can go wrong when hardening a WHMCS server?
Most problems are self-inflicted lockouts and broken integrations, not attacks.
- Lockouts. A firewall rule or an IP restriction can shut you out. Keep provider console access and a second session open while you work.
- Broken callbacks. Renaming the admin folder or forcing HTTPS can break gateway and mail-provider callbacks. Search your settings for the old path.
- Failed updates. With schema privileges revoked, the next update or module activation fails until you grant them back.
- A narrow CSF fork. cPanel's fork exists for cPanel servers and covers critical fixes only, with no new features and no configuration support.
- Panel vulnerabilities. A firewall cannot fix a flaw in software you expose. My CVE-2026-41940 advisory shows why WHM should never face the open internet.
A checklist is not an audit. If you want an outside review of the WHMCS install itself, that is my WHMCS security audit, and the Linux and cPanel layer is covered under server security hardening.
Last reviewed 12 October 2026. Disclosure: I sell WHMCS, server security and web development work, so I am not neutral. I am CTO at ElySpace, a freelance developer for Marmaids LLC, and founder of WHMCSPilot and MagizAI. Settings come from the WHMCS 9.1 documentation as I read it on that date.