A cPanel server running CloudLinux, LiteSpeed Web Server, ModSecurity, and Imunify360 has several overlapping control layers. That is useful for shared hosting, but it can make a simple WordPress error deceptive. The PHP binary tested over SSH may not be the PHP version serving the domain, and a WordPress publishing failure may be caused by a WAF rule rather than WordPress itself.
This guide reconstructs two real incidents from that stack:
- WordPress reported that the MySQL extension was missing even though PHP 8.3 packages were installed.
- Saving or publishing an article returned HTTP 403 until ModSecurity was disabled.
The goal is to establish evidence at each layer and make the narrowest possible correction. Do not add duplicate PHP directives or disable the WAF globally as a first response.
Understand Which Component Owns Each Decision
| Component | Primary role in this incident |
|---|---|
| cPanel/WHM | Manages domains, EasyApache packages, MultiPHP assignments, handlers, and ModSecurity vendors |
| CloudLinux | Provides the operating-system and hosting-account isolation layer |
| LiteSpeed | Serves web requests and launches the PHP handler used by the virtual host |
| EasyApache 4 | Provides cPanel-managed PHP versions and extensions |
| ModSecurity | Inspects HTTP requests against WAF rules |
| Imunify360 | Supplies security controls and can manage domain-specific disabled ModSecurity rules |
| WordPress | Reports the PHP capability it sees and sends editor POST or REST requests |
An SSH command that runs /opt/cpanel/ea-php83/root/usr/bin/php proves what that binary can load. It does not prove which PHP version, configuration file, or handler processed a request for a particular domain.
Incident 1: WordPress Says the MySQL Extension Is Missing
The server produced the following evidence:
- The PHP 8.3 MySQL-related EasyApache packages were already installed.
/opt/cpanel/ea-php83/root/usr/bin/php -mreturnedmysqli.- The
mysqli.somodule existed on disk. - LiteSpeed restarted successfully and was serving as
httpd. - The package manager also warned that
/etc/yum.repos.d/ea4.repocould not be loaded.
The first four observations make “install mysqli again” an unlikely solution. The more useful hypothesis is a mismatch between the tested CLI binary and the PHP environment assigned to the website.
Do Not Append extension=mysqli Blindly
If php -m already lists mysqli, appending another extension=mysqli directive does not solve a virtual-host mismatch. It can create duplicate-loading warnings and moves configuration away from the package-managed state. EasyApache should own the extension package and its normal module configuration.
cPanel documents PHP extension selection through WHM’s EasyApache 4 interface and package discovery through repoquery in its current PHP options documentation. Use those supported mechanisms instead of creating an arbitrary second INI file.
Step 1: Confirm the Domain’s Assigned PHP Version
The easiest method is WHM > Software > MultiPHP Manager or the equivalent cPanel interface. cPanel’s MultiPHP Manager documentation confirms that every virtual host can have its own installed PHP version or inherit the system default.
From SSH, cPanel documents this check:
grep phpversion /var/cpanel/userdata/USERNAME/DOMAIN
Expected output for this case is:
phpversion: ea-php83
Replace USERNAME and DOMAIN with the actual cPanel account and domain. If the domain inherits another version, fix the assignment in MultiPHP Manager and retest before changing extension files.
Step 2: Confirm the Active Handler
Display cPanel’s current PHP handler configuration with the documented command:
/usr/local/cpanel/bin/rebuild_phpconf --current
The cPanel rebuild_phpconf documentation identifies --current as a read-only display of the active handler settings. On LiteSpeed, confirm that the PHP version selected for the domain is mapped to the intended supported handler. Make changes through WHM’s MultiPHP Manager rather than editing generated handler files manually.
Step 3: Compare the Web SAPI with the CLI SAPI
Use a temporary diagnostic endpoint that reveals only the facts needed for the test:
<?php
header('Content-Type: text/plain');
printf("PHP=%s\n", PHP_VERSION);
printf("SAPI=%s\n", PHP_SAPI);
printf("mysqli=%s\n", extension_loaded('mysqli') ? 'loaded' : 'missing');
printf("ini=%s\n", php_ini_loaded_file() ?: 'none');
Protect the file by source IP or test it during a short maintenance window. Remove it immediately after the check. A public phpinfo() page exposes far more server information than is necessary.
Compare its output with:
/opt/cpanel/ea-php83/root/usr/bin/php -v
/opt/cpanel/ea-php83/root/usr/bin/php -m | grep -i '^mysqli$'
/opt/cpanel/ea-php83/root/usr/bin/php --ini
If the website reports a different PHP version or INI path, correct the virtual-host assignment or handler. If the web request uses PHP 8.3 but reports mysqli=missing, return to EasyApache 4, verify the package selected for PHP 8.3, provision the supported profile, and then restart LiteSpeed through the supported service control.
Step 4: Treat the ea4.repo Warning as a Separate Fault
The repository warning may prevent future EasyApache updates even if the already-installed PHP module works today. Do not delete or replace the repository file with an unverified copy.
Start with a non-destructive package check:
/usr/local/cpanel/scripts/check_cpanel_pkgs --list-only
cPanel documents that check_cpanel_pkgs can detect missing, outdated, or altered cPanel-managed packages and can repair detected package problems with --fix. The same documentation notes that it does not validate every configuration or documentation file. Review the result, verify CloudLinux and cPanel repository configuration, and use cPanel or CloudLinux support if the repository definition itself is damaged.
Do not combine repository repair, PHP handler changes, and WAF changes in one maintenance action. Resolve and validate each layer separately.
Incident 2: WordPress Publishing Returns HTTP 403
In the second incident, WordPress could display the editor, but saving or publishing a post failed with 403. Disabling ModSecurity made the operation work. The corresponding WAF event identified rule 941100, described as an XSS detection triggered during a POST to the WordPress editor.
That evidence points to a false positive in the request body, but it does not justify turning off ModSecurity for the entire server. The content, request route, matched variable, rule vendor, and domain must be recorded first.
Find the Exact Blocking Event
Open WHM > Security Center > ModSecurity Tools > Hits List and reproduce the failure once. Filter by the event time, domain, source IP, and request URI. Open the hit and record:
- Rule ID and rule vendor.
- HTTP method and URI.
- Matched variable or request component.
- Message and severity.
- Unique transaction or audit-log identifier.
- Whether another rule in the same request also blocked the transaction.
cPanel’s current ModSecurity Tools documentation notes that Hits List displays the most relevant hit, while the full logs may contain additional events. It also allows a vendor rule hit to be reported to the vendor when the vendor supports reports.
Do not assume every WordPress 403 is rule 941100. A different article, plugin, REST route, or security-vendor update can trigger a different rule.
Apply the Narrowest Supported Exception
Use this order of preference:
- Report the false positive to the ruleset vendor. Include the transaction details and a sanitized reproduction. A vendor correction protects other customers and survives future ruleset updates.
- Disable the confirmed rule only for the affected domain. Imunify360 supports domain-scoped disabled ModSecurity rules in its interface and CLI.
- Create a target exclusion only with complete audit-log evidence. A target exclusion should remove the specific WordPress field from inspection by that rule, not remove the entire rule from every WordPress request. Have the ruleset vendor or an experienced ModSecurity administrator build and test it against the exact matched variable.
- Use Detection Only briefly for diagnosis, not as the permanent security posture.
Imunify360 documents the following domain-scoped form for a ModSecurity rule that interferes with legitimate traffic:
imunify360-agent rules disable \
--id 941100 \
--plugin modsec \
--name 'Confirmed WordPress editor false positive' \
--domains example.com
Replace the rule ID and domain with the values from the actual incident. Check the result with:
imunify360-agent rules list-disabled
The current Imunify360 CLI documentation states that the --domains option scopes disabled ModSecurity rules to specified domains. This is safer than disabling ModSecurity globally, although a vendor-supplied target exclusion can be narrower still.
Why a Broad WordPress Whitelist Is Risky
Disabling rule 941100 across /wp-admin/ and /wp-json/ for every hosted domain removes an XSS control from large, high-value request surfaces. Disabling all of ModSecurity for the domain is broader again. Either action can hide a real exploit attempt that resembles the original false positive.
The exception should be traceable to one confirmed incident, have an owner, and include a review date. Re-enable it after the vendor fixes the rule or the content pattern no longer triggers it.
Validate the ModSecurity Fix
After applying the exception:
- Keep ModSecurity enabled globally and for the affected domain.
- Save the exact article that previously failed.
- Publish a simple text-only test article.
- Save content containing normal links, code blocks, and Gutenberg markup.
- Confirm that no new 403 appears for the same transaction.
- Confirm that unrelated WAF rules still block a safe vendor-provided test case; do not send real attack payloads to production.
- Record the disabled rule, domain, reason, date, and vendor ticket.
- Review the exception after the next Imunify360 or ruleset update.
Make ModSecurity Visible to cPanel Users
If account owners need the domain-level ModSecurity control in cPanel, enable ModSecurity Domain Manager in the feature list assigned to their package:
- Open WHM > Packages > Feature Manager.
- Edit the feature list used by the relevant accounts.
- Enable ModSecurity Domain Manager.
- Save the list and confirm that the package/account uses it.
cPanel’s ModSecurity user-interface documentation confirms that the Apache ModSecurity module must be installed and the ModSecurity Domain Manager feature must be enabled. cPanel also strongly recommends keeping ModSecurity enabled for all domains and disabling it only while troubleshooting.
Giving users a switch does not replace central monitoring. If users can disable the WAF, alert on disabled domains and establish a policy for re-enabling protection after diagnosis.
Operational Checklist for This Stack
PHP and LiteSpeed
- Confirm the domain’s PHP version in MultiPHP Manager.
- Confirm the active handler with
rebuild_phpconf --current. - Compare web-SAPI and CLI-SAPI versions, modules, and INI paths.
- Manage extensions through EasyApache packages.
- Remove temporary diagnostic files immediately.
- Repair repository warnings as a separate maintenance task.
ModSecurity and Imunify360
- Keep one supported, actively updated ruleset strategy.
- Use Hits List and full audit logs to identify the exact event.
- Report false positives to the vendor.
- Scope disabled rules to the affected domain whenever possible.
- Prefer a tested target exclusion over disabling an entire rule.
- Inventory and review every exception.
Imunify360’s current cPanel integration guidance recommends avoiding overlapping third-party ModSecurity vendors when the Imunify360 ruleset is used, because duplicate rulesets can increase false positives. Do not switch vendors during an active incident without first exporting the current configuration and understanding the protection change.
Frequently Asked Questions
If php -m shows mysqli, why does WordPress say it is missing?
The CLI command may use a different PHP binary, handler, or INI configuration from the domain’s web request. Confirm the virtual host’s assigned PHP version and test the web SAPI directly.
Should I add extension=mysqli manually?
Not when the EasyApache module is already installed and loaded. First identify the PHP environment serving the website. Let EasyApache manage packaged extensions unless cPanel documentation or support identifies a specific configuration defect.
Should I disable ModSecurity for WordPress?
No. Identify the exact blocking rule and matched request component. Use a vendor fix, a domain-scoped rule exception, or a tested target exclusion. Keep the rest of the WAF active.
Is rule 941100 always a false positive in the editor?
No. It is an XSS-detection rule and can identify malicious input. Treat it as a false positive only after correlating the exact legitimate request with the audit event.
Conclusion
The two errors came from different layers. The MySQL-extension message required comparing the website’s PHP runtime with the PHP 8.3 CLI runtime. The publishing 403 required correlating a WordPress request with a specific ModSecurity event and narrowing the exception to the affected domain.
This evidence-first method keeps the server supportable. It avoids duplicate PHP configuration, preserves WAF coverage, and creates a clear record for the next cPanel, LiteSpeed, CloudLinux, or Imunify360 update.