Download our Free HTML Cheat Sheet - Quick Reference for Beginners

Stop WHMCS Bot Registrations with Custom Security Hooks

Stop WHMCS Bot Registrations with Custom Security Hooks
Posted 02 April 2025
Updated 23 September 2026

If your WHMCS installation is getting bombarded with fake accounts, you've probably already tried the obvious fix - disabling registration in the admin panel - and found it didn't actually stop anything. That's because WHMCS has several separate entry points a new account can be created through, and disabling registration in the admin panel only closes one of them.

This guide covers a hook-based solution that blocks every real entry point, explains exactly which WHMCS hooks genuinely support blocking an action and which don't (a distinction that matters, because several popular versions of this exact tutorial floating around the web use hooks that either don't exist or silently can't block anything), and covers what to do about the one entry point - the API - that hooks can't protect at all.

Why disabling registration in the admin panel isn't enough

WHMCS offers several distinct paths to creating a new client account:

  • The standard registration page (register.php)
  • The order/checkout process, when a visitor checks out as a new customer (cart.php)
  • The API's AddClient command
  • Manual creation in the admin panel (not something bots can reach, so not covered here)

The "Registration" toggle in General Settings only affects the first of these. Block that one path and a bot - or a script scanning for open registration forms generally, not even targeting you specifically - will simply hit the cart or the API instead. A complete fix needs to cover all of them.

The multi-hook solution

WHMCS hooks let you run your own code when a specific event happens, and some of them can stop the event from completing. The important detail - the one most bot-blocking tutorials get wrong - is that not every hook supports blocking. Some only let you add data to a template; if you try to abort an action from one of those, nothing happens, and you won't find out until a bot account shows up despite your "block."

Here's what each hook in this solution actually supports, confirmed against WHMCS's own developer documentation:

  • ClientAreaPageRegister - fires on the registration page (covers both loading the form and submitting it, since both happen on the same script). Doesn't have a built-in block response, but a plain header() redirect plus exit inside the hook works regardless, and is the documented way to do it.
  • ClientAreaPageCart - fires on the cart page. Same situation: no built-in block response, but header() + exit works.
  • ShoppingCartValidateCheckout - fires right before an order and invoice get created. This one genuinely supports blocking - return a string (or array of strings) and WHMCS shows it as an error and stops checkout.
  • ClientAdd - fires as a client is being added. WHMCS's own documentation for this hook says plainly: "No response supported." Nothing you return from it can stop the account being created - it already exists in the database by the time this hook runs. It's useful as a monitoring backstop (see below), not as a block.

Two hook names that show up in other versions of this tutorial - ClientRegister and APIRequest - aren't real WHMCS hooks at all. add_hook() silently does nothing for a hook name WHMCS doesn't recognise, so code built around them looks like it's doing something and isn't. The code below leaves them out entirely.

Complete hook code

Create a file called blockregistrations.php and save it in your /includes/hooks/ directory:

<?php

// ========== CONFIGURATION SECTION ==========
// Set the date when registrations should become available again
$enableRegistrationsAfter = "2025-06-30";

// Set to true if you want email notifications of blocked attempts
$sendEmailNotifications = true;

// Email where notifications should be sent
$notificationEmail = "your-email@example.com";

// ========== NO NEED TO EDIT BELOW THIS LINE ==========

function sendBlockNotification($entryPoint, $email = "unknown", $ip = "unknown") {
global $sendEmailNotifications, $notificationEmail;
if (!$sendEmailNotifications || empty($notificationEmail)) return;
$subject = "WHMCS - Registration Attempt Blocked";
$message = "A registration attempt was blocked.\n\n";
$message .= "Entry Point: " . $entryPoint . "\n";
$message .= "IP Address: " . $ip . "\n";
$message .= "Email: " . $email . "\n";
$message .= "Date/Time: " . date("Y-m-d H:i:s") . "\n";
mail($notificationEmail, $subject, $message);
}

function shouldBlockRegistrations() {
global $enableRegistrationsAfter;
return strtotime($enableRegistrationsAfter) > time();
}

// 1. Block the registration page - covers both loading the form and submitting it,
// since both happen on this same hook point.
add_hook('ClientAreaPageRegister', 1, function($vars) {
if (shouldBlockRegistrations()) {
logActivity("BLOCKED - Registration page access from IP: " . $_SERVER['REMOTE_ADDR']);
sendBlockNotification("Registration Page", "unknown", $_SERVER['REMOTE_ADDR']);
header("Location: index.php");
exit;
}
});

// 2. Block guests reaching the cart - a common way to register via checkout
// instead of the registration page.
add_hook('ClientAreaPageCart', 1, function($vars) {
if (shouldBlockRegistrations()) {
if (!isset($_SESSION['uid']) || empty($_SESSION['uid'])) {
logActivity("BLOCKED - Cart access by non-logged in user - IP: " . $_SERVER['REMOTE_ADDR']);
sendBlockNotification("Cart Access", "unknown", $_SERVER['REMOTE_ADDR']);
header("Location: index.php");
exit;
}
}
});

// 3. Block checkout completion for new-customer signups. This hook genuinely
// supports blocking - return a string and WHMCS shows it as an error.
add_hook('ShoppingCartValidateCheckout', 1, function($vars) {
if (shouldBlockRegistrations()) {
if (empty($vars['custtype']) || $vars['custtype'] == 'new') {
logActivity("BLOCKED - Cart checkout registration - IP: " . $_SERVER['REMOTE_ADDR']);
sendBlockNotification("Cart Checkout", "unknown", $_SERVER['REMOTE_ADDR']);
return 'New account registration is currently disabled.';
}
}
});

// 4. Monitoring backstop only. ClientAdd's own documentation says "No response
// supported" - it cannot block account creation, it fires after the account
// already exists. This just tells you if something got through the blocks above.
add_hook('ClientAdd', 1, function($vars) {
if (shouldBlockRegistrations()) {
$email = $vars['email'] ?? 'unknown';
logActivity("WARNING - A new client account was created despite registration being disabled - Email: " . $email);
sendBlockNotification("Client Created Despite Block (investigate)", $email, $_SERVER['REMOTE_ADDR'] ?? 'unknown');
}
});
?>

Why the API isn't covered by a hook

There's no hook that fires before an API command runs and lets you block it - it isn't a gap in this guide, it's a gap in what hooks can do in WHMCS. The AddClient API command doesn't route through the ClientAreaPageRegister or ShoppingCartValidateCheckout hooks above, since those are specific to the client-area web flow, not the API.

The correct fix is WHMCS's own API access control, not a hook: go to Setup > General Settings > Security and add your own trusted IP addresses to the API IP Access Restriction list. With at least one IP added, WHMCS rejects API requests from anywhere else outright - which stops a bot from ever reaching AddClient in the first place, rather than trying to catch it after the fact. For an extra layer, you can also set an API access key (a secret passphrase in configuration.php that every request must include).

Implementation guide

  1. Create the hook file: copy the code above, save it as blockregistrations.php, and place it in your /includes/hooks/ directory.
  2. Configure the settings at the top of the file: set the date registrations should reopen, whether you want email notifications, and the address to send them to.
  3. Set file permissions: 644 is typical, owned by the same user as your other WHMCS files.
  4. Restrict API access separately, as described above - the hook file doesn't cover this.
  5. Test it: load the registration page as a logged-out visitor, try checking out as a new customer, and check your activity log to confirm the blocks are firing.

Monitoring the solution

After implementing the hooks, check your activity logs to confirm they're actually firing. WHMCS's menu structure for this varies by version and by what's been customised, so if "Utilities > Logs > Activity Log" isn't where you expect it:

  • Try "Reports > Logs > Activity Log" or "Setup > Logs > Activity Log"
  • Go directly to /admin/systemactivitylog.php on your WHMCS admin URL
  • As a last resort, the tblactivitylog table is viewable directly through phpMyAdmin

Look for entries starting with "BLOCKED" - and if you ever see the "WARNING - a new client account was created despite registration being disabled" entry from the ClientAdd monitoring hook, that means something reached account creation through a path outside the three blocks above, and it's worth checking what that path was (a custom module, a third-party addon, or a customised template are the usual causes).

Is this the right approach for your installation?

Good fit: smaller hosting providers with infrequent new signups, installations where registration is handled manually anyway, sites under active bot attack, or temporarily locking things down during maintenance.

Not a good fit as-is: high-volume providers with regular legitimate signups, self-service platforms, or public marketplaces built on WHMCS - for those, adapt the approach below rather than blocking outright.

Adapting for high-volume signup environments

If legitimate signups happen regularly, a hard block isn't the right tool. Instead:

  • Replace the block with verification - add a CAPTCHA check or require email verification before the account is created, instead of refusing it outright.
  • Rate-limit by IP - allow a reasonable number of registrations per IP per day and only block once that's exceeded.
  • Add pattern-based checks - block obviously-automated patterns (multiple signups from one IP within seconds) without touching normal traffic.

All three fit into the same hook points used above - the difference is the condition inside each hook checks something more specific than "always block," and calls a CAPTCHA/rate-limit check instead of an unconditional shouldBlockRegistrations().

Additional security measures

These hooks handle the entry points, but they're one layer, not a complete security posture:

  • Enable CAPTCHA on your forms generally, not just as a bot-block substitute.
  • Use a fraud detection service (MaxMind and similar integrate directly with WHMCS) to flag suspicious orders even from accounts that do get through.
  • Clean your database periodically to remove whatever spam does get through, since no block is ever 100%.
  • Keep WHMCS updated - security patches for the platform itself matter more than any one hook.

Re-enabling registrations

The hook checks $enableRegistrationsAfter against the current date automatically, so registrations reopen on their own once that date passes. To reopen sooner, either edit that value in the hook file or delete the file entirely (keep a backup first).

Frequently asked questions

Does the ClientAdd hook actually block bot registrations?

No. WHMCS's own documentation for ClientAdd says "No response supported" - it fires after the account already exists, and nothing you return from it changes that. It's useful for logging/alerting that an account got through, not for stopping it.

Why doesn't disabling registration in the admin panel stop bots?

That setting only affects the standard registration page. Bots (and the order/checkout flow, and the API) can still create accounts through the other entry points it doesn't touch.

How do I block bots from creating accounts through the WHMCS API?

Not with a hook - there isn't one that fires before an API command executes. Use WHMCS's built-in API IP Access Restriction (Setup > General Settings > Security) to only allow requests from IPs you trust.

Will this break legitimate customer registrations?

Yes, deliberately, for as long as the block is active - that's the point. If you have regular legitimate signups, use the high-volume adaptation above (CAPTCHA/rate-limiting) instead of an outright block.

Are ClientRegister and APIRequest real WHMCS hooks?

No. Both appear in other copies of this tutorial elsewhere online, but neither is a documented WHMCS hook, and add_hook() does nothing when given a hook name WHMCS doesn't recognise. Code built around them doesn't error - it just silently never runs.

Rate this Article

 

Discussion

0 Comments

Be the first to start the discussion!