web-cp.net

"open source web hosting control panel"

← Back to Blog
Rate Limiting, Throttling and Tenant Isolation in Web-CP — Protecting Multi-Tenant Environments at the Apache Layer

Rate Limiting, Throttling and Tenant Isolation in Web-CP — Protecting Multi-Tenant Environments at the Apache Layer

2026-05-10 · Ethan Caldwell Techno Blog

The fundamental operational challenge of multi-tenant shared hosting is resource contention. A single tenant running a misconfigured application, executing a runaway script, or being targeted by a volumetric attack can consume the server's available bandwidth, process pool, or MySQL connection limit — degrading or denying service to every other account on the system. In a hosting environment where resource fairness is an implicit part of the service contract, this is both a technical failure and a business failure.

Web-CP's architecture — with its four-tier control panel model separating server, reseller, domain, and personal views — provides a natural framework for implementing resource controls at the appropriate level. The following covers the Apache-layer mechanisms that enforce those controls, and how they integrate with the Web-CP configuration model.

The Resource Contention Problem — What Actually Fails

Before implementing controls, it is worth being precise about what fails in an unprotected multi-tenant environment.

Connection pool exhaustion. Apache's prefork and worker MPMs maintain a bounded pool of active processes or threads. A single tenant endpoint receiving sustained traffic — legitimately or through an attack — can saturate the pool, causing 503 Service Unavailable responses for all other domains on the server. The attack does not need to target the server's IP directly; it only needs to overwhelm the process pool.

MySQL connection limits. The MySQL max_connections setting is global. A tenant application with a connection leak, or one that opens connections without properly pooling them, can exhaust the connection table. New connection attempts from other tenants receive Too many connections errors until the leaking application is corrected or isolated.

Bandwidth saturation. In environments without per-tenant bandwidth limits, a single domain serving large files or receiving high download traffic can saturate the server's network interface. Other tenants experience increased latency or timeouts without any indication of what is causing the degradation.

Log disk space. Apache and application-level error logs in multi-tenant environments can fill the log partition if a single tenant generates high error volume. A full log partition causes Apache to stop writing logs for all tenants, which breaks the audit trail for security investigations.

Apache mod_ratelimit — Per-Directory Bandwidth Control

mod_ratelimit provides bandwidth throttling at the Apache virtual host or directory level. It limits the rate at which Apache delivers response data to a client, expressed in KiB/s.

# Enable in a VirtualHost template — manageable through Web-CP's VirtualHost editor

<VirtualHost *:80>

ServerName tenant-domain.com

DocumentRoot /home/tenant/public_html

# Throttle all responses to 500 KiB/s per connection

<Directory /home/tenant/public_html>

SetOutputFilter RATE_LIMIT

SetEnv rate-limit 500

</Directory>

# Apply different limits to media directories

<Directory /home/tenant/public_html/downloads>

SetOutputFilter RATE_LIMIT

SetEnv rate-limit 200

</Directory>

</VirtualHost>

The rate-limit environment variable is expressed in KiB/s per connection, not per tenant. A tenant with many concurrent connections can still consume significant bandwidth — mod_ratelimit is a per-connection control, not a per-IP or per-account aggregate control. Combine it with connection limits (below) for aggregate bandwidth management.

Integration with Web-CP: Web-CP's VirtualHost templates can be modified at the server level to include mod_ratelimit directives for all hosted domains, or the reseller control panel can expose per-domain bandwidth settings that map to specific rate-limit values.

Apache mod_evasive — Connection Rate Limiting and DDoS Mitigation

mod_evasive monitors incoming request rates and blocks IPs that exceed configurable thresholds. It is a defensive control against HTTP flooding and application-layer DDoS attacks.

Tuning for shared hosting: The default thresholds in mod_evasive are appropriate for single-site servers but may produce false positives in multi-tenant environments where a single IP address may represent a NAT gateway serving many legitimate users. Tune DOSSiteCount upward (100–200) and DOSBlockingPeriod downward (5 seconds) for shared hosting to reduce false positive impact while still blocking sustained floods.

The tenant isolation implication: mod_evasive operates at the server level, not the tenant level. An IP blocked for excessive requests to tenant A is also blocked from accessing tenant B. In a hosting environment, this is usually the correct behavior — an IP conducting a HTTP flood is a server-level threat, not a single-tenant threat. Document this behavior in your terms of service.

Per-Tenant Process Limits with Apache MPM and PHP-FPM

The most effective tenant isolation mechanism is separating PHP execution into per-tenant process pools using PHP-FPM (FastCGI Process Manager). Each tenant's PHP processes run under the tenant's system user, with configurable process count and memory limits.

# /etc/php/8.3/fpm/pool.d/tenant-domain.conf

[tenant-domain]

user = tenant_user

group = tenant_user

; PHP-FPM socket for this pool

listen = /run/php/php8.3-fpm-tenant-domain.sock

listen.owner = www-data

listen.group = www-data

; Process management

pm = dynamic

pm.max_children = 10 ; Maximum concurrent PHP processes for this tenant

pm.start_servers = 2

pm.min_spare_servers = 1

pm.max_spare_servers = 3

pm.max_requests = 500 ; Restart worker after 500 requests — prevents memory leaks

; Resource limits

php_admin_value[memory_limit] = 128M

php_admin_value[upload_max_filesize] = 32M

php_admin_value[max_execution_time] = 30

php_admin_flag[allow_url_fopen] = off

; Restrict open_basedir to tenant's directory

php_admin_value[open_basedir] = /home/tenant_user/:/tmp/

The corresponding Apache VirtualHost configuration proxies PHP requests to the tenant-specific FPM socket:

<VirtualHost *:80>

ServerName tenant-domain.com

DocumentRoot /home/tenant_user/public_html

<FilesMatch "\.php$">

SetHandler "proxy:unix:/run/php/php8.3-fpm-tenant-domain.sock|fcgi://localhost"

</FilesMatch>

</VirtualHost>

What this achieves:

  • A runaway PHP process in one tenant's pool cannot consume process slots allocated to another tenant

  • Memory exhaustion in one pool does not affect other tenants' PHP execution

  • Security vulnerabilities in one tenant's application cannot be exploited to read files belonging to another tenant (enforced by open_basedir and separate user accounts)

  • Resource usage per tenant is visible and auditable through the FPM pool's status endpoint

Integration with Web-CP: Web-CP's domain account creation can be scripted to generate a PHP-FPM pool configuration file for each new domain, using the domain account's system user as the pool user. The server control panel's templating system provides the hook for this automation.

MySQL Per-Tenant Resource Controls

MySQL's resource control mechanisms are less granular than Apache's, but several controls are available at the user level:

MAX_USER_CONNECTIONS is the most important limit for shared hosting. Setting it to 20 per tenant prevents any single tenant from exhausting the server's max_connections limit. The appropriate value depends on the tenant's application profile — a WordPress site with persistent connections needs fewer than an application with poorly managed connection pooling.

Monitoring query performance: Enable the MySQL slow query log to identify tenants running expensive queries:

Queries exceeding the threshold are logged with the database user, query text, and execution time. In a multi-tenant environment, this is the primary tool for identifying tenants whose database usage is degrading shared performance.

Log Rotation and Disk Isolation

Log disk space exhaustion is a common and underappreciated failure mode in shared hosting. Each Apache VirtualHost generates at least one access log and one error log. In a high-traffic environment, a single misbehaving tenant can generate gigabytes of log data per day.

# Per-tenant log configuration in VirtualHost template

<VirtualHost *:80>

ServerName tenant-domain.com

# Separate log files per domain

ErrorLog /var/log/apache2/tenant-domain-error.log

CustomLog /var/log/apache2/tenant-domain-access.log combined

# Pipe logs through rotatelogs to limit individual file size

ErrorLog "|/usr/bin/rotatelogs /var/log/apache2/tenant-domain-error.%Y%m%d.log 86400"

CustomLog "|/usr/bin/rotatelogs /var/log/apache2/tenant-domain-access.%Y%m%d.log 86400" combined

</VirtualHost>

Configure logrotate to enforce retention limits:

# /etc/logrotate.d/web-cp-domains

/var/log/apache2/tenant-*-access.*.log {

daily

rotate 14

compress

delaycompress

missingok

notifempty

sharedscripts

postrotate

/usr/bin/systemctl reload apache2 > /dev/null 2>&1 || true

endscript

}

Mounting the log partition on a separate filesystem — either a separate physical volume or a dedicated LVM logical volume — ensures that log growth in one area cannot fill the root filesystem and cause system-level failures.

Operationalizing Tenant Isolation

The controls above are individually useful but operationally significant only when they are applied consistently. In a Web-CP environment, consistency requires that:

  1. VirtualHost templates include rate limiting and FPM proxy directives by default — not as optional additions

  2. PHP-FPM pool configuration is generated automatically when a domain account is created

  3. MySQL user creation includes resource limits as part of the standard provisioning script

  4. Log rotation is applied globally through a logrotate configuration that matches all tenant log patterns

The server control panel in Web-CP provides the configuration entry points for VirtualHost and DNS templates. PHP-FPM pool management and MySQL user provisioning require scripting at the server level, but they can be triggered from Web-CP's account creation hooks.

Tenant isolation is not a feature that can be retrofitted to a running hosting environment without operational disruption. It is most effectively built into the provisioning process from the beginning, where the marginal cost of applying it to each new domain is near zero.