Panellicense

cPanel account suspension: WHM workflow, automation, and unsuspension

How cPanel suspension actually works under the hood, the WHM and API workflows, custom reasons and pages, and the gotchas that bite when you unsuspend an account.

10 min readUpdated 2026-05-25cpanel · whm · suspension · automation
schema: HowToschema: FAQPageschema: BreadcrumbList

Suspension is the lever you pull when a customer's invoice is 14 days overdue, when an account is mid-compromise, or when abuse has just been reported and you want to stop the bleeding before you investigate. It's reversible, it's logged, and it's the single most common write operation a billing system performs against WHM.

The mechanics aren't obvious, though — suspension touches at least five subsystems (shell, FTP, mail, MySQL, Apache), and the behaviour around mail and DNS in particular catches resellers out the first time a suspended customer asks why a bounce was sent to their sender.

What suspension actually does

When you suspend bob, cPanel performs these operations in order:

  1. Locks the system user (passwd -l bob), which kills SSH password and key auth.
  2. Sets the shell to /usr/local/cpanel/bin/noshell so even an existing SSH key returns an error message instead of a shell.
  3. Adds the account to /var/cpanel/suspended/ with a file named after the account that contains the suspension reason.
  4. Rewrites the Apache vhost so HTTP/S requests return the suspended-account page (the "Account Suspended" template at /var/cpanel/webtemplates/root/english/suspended.tmpl, or your custom override).
  5. Disables outbound mail by setting /etc/exim_outgoing_remote_smtp_deny flags and flagging the user in /etc/cpanel/local/suspendedmail (depending on version).
  6. Disables FTP via Pure-FTPd or ProFTPd's per-user shell check.
  7. Leaves MySQL untouched — databases stay accessible from other accounts and from any service authenticating with stored credentials.

That last point is the most common surprise. A suspended account's WordPress site still hits MySQL fine; if you've redirected the vhost to a "suspended" page, the database keeps running queries from whatever scripts still reach it (cron, the cPanel API, restored backups). If you need to fully cut access, suspend the cPanel account and revoke MySQL grants — but think first, because re-granting them on unsuspension is manual.

Mail behaviour is the other gotcha. By default, suspending an account stops outbound mail but inbound mail still flows into the mailboxes. Customers expect "I'm suspended so I won't get email" and discover six weeks of unread mail when they pay. To bounce inbound too, enable Suspend incoming mail for suspended accounts under WHM → Server Configuration → Tweak Settings.

Suspend from WHM

Under WHM → Account Functions → Manage Account Suspension, pick the user, type a reason, and click Suspend. The reason is visible to other resellers managing the same account and ends up in /var/cpanel/suspended/<user>. It is not shown to the customer.

A few non-obvious checkboxes on that page:

  • Disallow un-suspension by reseller — gates the reseller who owns the account from un-suspending it themselves. Use this when you're suspending for ToS/abuse and the reseller has been the abuse vector. See the reseller ACL reference for which ACLs map to suspend/unsuspend permissions.
  • Enable Email Notification on Suspension — sends to the reseller's contact email, not the customer's. To notify the customer, your billing system has to do it, since WHM has no concept of the customer's email outside the cPanel contact field.

Suspend from the CLI or API

WHM exposes both legacy and modern entry points. The modern call:

whmapi1 suspendacct user=bob reason="Non-payment - invoice 4821" disallow=1

The legacy script still works and is what many WHMCS/Blesta modules call under the hood:

/scripts/suspendacct bob "Non-payment - invoice 4821" 1

The trailing 1 means "disallow reseller unsuspension." Omit or set to 0 to allow it.

For machine-driven suspensions from a billing system, use the API with a scoped WHM API token rather than root credentials, with a custom ACL list that includes suspend-acct but not kill-acct. If your billing system's provisioning module ever goes rogue, you want it to be able to pause accounts, not delete them.

To check the current state without unpacking the suspended/ directory:

whmapi1 listsuspended | head -20
whmapi1 accountsummary user=bob | grep -i suspend

Customise the suspension page and reason templates

The default suspended.tmpl is plain HTML that says "Account Suspended" — it leaks zero information, which is the point, but it also looks like a hosting-default error page. Most resellers want one of two things: a branded "this site is temporarily unavailable" page, or a clear "your hosting is past due, click here to pay" page for billing suspensions.

You can override per-reseller (preferred) or globally:

# Per-reseller
mkdir -p /var/cpanel/webtemplates/<reseller>/english/
cp /var/cpanel/webtemplates/root/english/suspended.tmpl \
   /var/cpanel/webtemplates/<reseller>/english/suspended.tmpl
# Edit the copy. Variables [% ACCTNAME %] and [% RESELLER %] are interpolated.

# Global override (affects all suspended accounts not covered by a reseller template)
cp /var/cpanel/webtemplates/root/english/suspended.tmpl \
   /var/cpanel/webtemplates/root/english/suspended.tmpl.bak
# Edit /var/cpanel/webtemplates/root/english/suspended.tmpl in place

Apache reads the template on every request to a suspended vhost — no rebuild needed. Cached responses on a CDN in front (Cloudflare, QUIC.cloud) will still serve the old content until they expire, so purge after editing.

For the reason strings shown in WHM dropdowns, edit /var/cpanel/suspendedreasons.json. Pre-populating it with your own common reasons (Non-payment, Abuse - phishing, Abuse - spam, Investigation - awaiting customer response) makes suspension consistent across staff and grep-able later when you're producing a report for an abuse complaint.

Automate from your billing system

WHMCS and Blesta with the cPanel provisioning module both call suspendacct on overdue invoices and unsuspendacct on payment. Two things to verify when you wire this up:

  • The token's IP allow-list should include the billing server's IP and only the billing server's IP. A leaked WHMCS token with full suspend-acct access on the internet is a fast way to have someone suspend your entire customer base.
  • The "suspension days past due" setting in the billing system should be coordinated with your dunning emails. A standard pattern: day 0 invoice issued, day 7 reminder, day 14 second reminder + late fee, day 21 suspend, day 45 terminate. Anything tighter produces angry customers; anything looser is bad-debt territory.

For bespoke automation outside the billing system, WHM standardised hooks fire on Cpanel::Accounts::suspendacct and unsuspendacct. Use these to push a Slack message into your ops channel, append to an audit log, or kick off a backup before suspension so you have something to restore from if the customer is wrongly suspended.

Bulk suspend and reseller suspensions

Suspending a whole reseller suspends all their owned accounts in a single operation:

whmapi1 suspendreseller user=acmehost reason="Reseller payment overdue"

This is materially different from suspending only the reseller's own cPanel account — suspendreseller walks the ownership tree and suspends every child account too. It's the right tool for "this reseller hasn't paid in 60 days," and the wrong tool for "this reseller's main cPanel account was compromised but their customers are fine."

For bulk operations across non-reseller accounts, drive a loop against the API:

for u in $(whmapi1 --output=jsonpretty listaccts | jq -r '.data.acct[] | select(.partition=="legacy-vol") | .user'); do
  whmapi1 suspendacct user="$u" reason="Migration freeze - ticket 9912"
done

This pattern — filter on a property, loop and suspend — is the workhorse for migrations, where you suspend the source to prevent customer changes between rsync and DNS cutover. Unsuspend on the destination once the new home is live.

Unsuspend — what comes back and what doesn't

whmapi1 unsuspendacct user=bob
# or
/scripts/unsuspendacct bob

Unsuspension reverses the steps from earlier in roughly the same order. Things that generally do come back automatically: shell access, FTP, the original vhost, outbound mail, the original cron entries (they were never removed; cron jobs continue to run during suspension unless you also disable them).

Things that may not come back without manual intervention:

  • MySQL grants you revoked outside of cPanel. Suspension doesn't touch them and neither does unsuspension. If you ran REVOKE ALL during the abuse investigation, you need to re-grant manually.
  • API tokens the user issued from cPanel — these are preserved across suspension, so any external system using a customer's API token continues to work during suspension unless the token's IP allow-list happens to be cached behind the suspended vhost.
  • Mail queue. If inbound mail was suspended too, queued bounces accumulate in /var/spool/exim and are delivered on unsuspension — sometimes hundreds of bounce notifications hit the customer's inbox at once. Optionally drain the queue with exim -bp | grep bob and exim -Mrm <msgid> before unsuspending.
  • Cached suspension pages on Cloudflare or QUIC.cloud. Purge the customer's domain cache after unsuspending or visitors get the suspension page for up to the cache TTL.

Next steps

Does a suspended cPanel account still receive email?+
By default, yes — outbound mail is blocked but inbound mail is still delivered to the mailboxes. To bounce inbound mail too, enable 'Suspend incoming mail for suspended accounts' in WHM → Tweak Settings. Customers expect suspension to stop everything, so most resellers turn this on.
Can a suspended cPanel account still access MySQL?+
Yes. Suspension does not touch MySQL grants. The website's vhost is redirected to the suspended page, but any process that still has database credentials (cron jobs, restored backups, external scripts) can still connect. Revoke grants separately if you need a hard cut, and remember to re-grant on unsuspension.
How do I bulk-suspend a reseller's accounts?+
Use whmapi1 suspendreseller user=<reseller> reason='<reason>'. This walks the ownership tree and suspends every account owned by that reseller. To suspend only the reseller's own cPanel account without touching their customers, use suspendacct on the reseller's account directly.
Does cPanel suspension delete data?+
No. Suspension is purely an access lock — home directory, mail store, databases, and DNS zones are all preserved untouched. Termination (killacct) is the destructive operation. Most resellers suspend at 21 days past due and only terminate at 45 or 60 days, with a final dunning email between the two.
Why does my custom suspended page not show?+
Two common causes. First, you edited the file but Apache's serving from a CDN cache — purge Cloudflare or QUIC.cloud for the domain. Second, the reseller has their own template in /var/cpanel/webtemplates/<reseller>/english/suspended.tmpl that takes precedence over the global one. Check both before assuming the edit didn't apply.
Can a suspended account still log into cPanel?+
No. The suspension flag blocks cPanel and Webmail logins for the user. WHM logins are unaffected — they're tied to root or reseller accounts, not the suspended cPanel user — so resellers and admins can still manage the account during suspension.
Switch in an afternoon

Switch from your current reseller — free.

We migrate active cPanel, Plesk, LiteSpeed and CloudLinux licenses from any reseller. We prorate the first month so you never pay twice, and your customers see zero downtime during the swap.