Why look at this
A control panel's most consequential output is the web-server configuration it writes for you. It decides which requests are refused, how PHP is reached, what is cached and for how long, which headers every visitor receives, and how much of the WordPress attack surface is closed before PHP ever runs. Two panels can advertise the same features and write very different server blocks.
This study puts the block auraPanel renders for a WordPress site next to the WordPress template CloudPanel publishes, directive by directive. It is a reading of configuration, not a benchmark: nothing here says which serves pages faster.
What was compared
- auraPanel: the file nginx loads for a production WordPress site on this host, rendered by the panel from its
wordpresstemplate with the site's options (force HTTPS on, bad-bot block on, Cloudflare edge shield on, page cache off, login throttling off). The domain is shown asexample.com; a site-specific drop-in the operator had added is removed. - CloudPanel: v2/WordPress/WordPress from
cloudpanel-io/vhost-templates, which CloudPanel's vhost documentation says is "automatically updated every night". Placeholders such as{{server_name}}are CloudPanel's own.
A template is not a rendered file. CloudPanel's global nginx.conf was not reviewed, so a directive absent from its template may be set there; the table says "not in template" rather than "absent". auraPanel's global settings are quoted where they matter.
auraPanel's rendered block
map $http_user_agent $aurapanel_bad_bot_example_com {
default 0;
"~*(ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)" 1;
}
server {
listen 80;
listen [::]:80;
server_name example.com;
location ^~ /.well-known/acme-challenge/ {
alias /var/lib/aurapanel/acme/;
default_type "text/plain";
try_files $uri =404;
}
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
root /home/example/htdocs/example.com;
index index.php index.html;
ssl_certificate /etc/aurapanel/ssl/example.com.crt;
ssl_certificate_key /etc/aurapanel/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
location ^~ /.well-known/acme-challenge/ { alias /var/lib/aurapanel/acme/; default_type "text/plain"; try_files $uri =404; }
access_log /home/example/logs/access.log;
error_log /home/example/logs/error.log;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
server_tokens off;
location ~* \.log(\.\d+|\.gz)?$ { deny all; access_log off; log_not_found off; }
include /etc/nginx/aura-redirects.d/example.com/*.conf;
client_max_body_size 64m;
if ($aurapanel_bad_bot_example_com) { return 403; }
# Cloudflare Edge Shield — accept origin traffic from the CDN edge only
set_real_ip_from 173.245.48.0/20; # … 21 more Cloudflare ranges
real_ip_header CF-Connecting-IP;
real_ip_recursive on;
set $edgeshield_block 1;
if ($edgeshield_cloudflare) { set $edgeshield_block 0; }
if ($edgeshield_self) { set $edgeshield_block 0; }
if ($uri ~ "^/\.well-known/acme-challenge/") { set $edgeshield_block 0; }
if ($edgeshield_block) { return 403; }
location ~ /\.(?!well-known) { deny all; }
location = /xmlrpc.php { deny all; }
location ~ ^/wp-content/uploads/.*\.php$ { deny all; }
if (!-e $request_filename) {
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
rewrite ^(/[^/]+)?(/wp-.*) $2 last;
rewrite ^(/[^/]+)?(/.*\.php)$ $2 last;
}
location / { try_files $uri $uri/ /index.php?$args; }
location ~* \.(css|js|jpg|jpeg|gif|png|ico|svg|woff|woff2|ttf|otf|webp|webm|mp4)$ {
rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 break;
rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 break;
expires max;
access_log off;
add_header Cache-Control "public, immutable";
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php-fpm/example.com.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_read_timeout 300s;
}
} Globally, on the same host: server_tokens off, gzip on (level 5, 1 KB minimum, the usual text types), TLS session cache shared with tickets off, open_file_cache, a limit_req_zone keyed on host plus client IP for WordPress, Joomla and Drupal login paths at 2 requests a second (applied only when a site turns login throttling on), and the FastCGI and proxy cache zones that a site's page cache uses when enabled. nginx 1.31.2 here is built with http_v2, http_v3, realip and gzip_static; no Brotli or PageSpeed modules.
Directive by directive
| Concern | auraPanel (rendered) | CloudPanel (template) |
|---|---|---|
| Server blocks | Two: port 80 serves only the ACME path and redirects; port 443 serves the site. | One block listening on 80 and 443; HTTPS enforced by an if ($scheme != "https") rewrite inside it. |
| HTTP versions | http2 on. HTTP/3 is compiled into the host's nginx but the panel does not enable it. | listen 443 ssl http2. The docs offer HTTP/3 on Ubuntu 24.04 and Debian 12 with nginx 1.26; it is not in this template. |
| HTTPS redirect | return 301 https://$host$request_uri — query string preserved. | rewrite ^ https://$host$uri permanent — $uri carries no query string, so ?utm=… is dropped on the redirect. |
| TLS protocols and ciphers | TLS 1.2 and 1.3 in the block; server cipher preference off; session tickets off globally. | Not in template. |
| ACME challenges | Served from a panel-owned directory via alias, on both ports, exempt from the edge shield. | location ~ /.well-known { auth_basic off; allow all; } — served from the document root. |
| Security headers | HSTS (1 year), X-Content-Type-Options, X-Frame-Options (per-site policy), Referrer-Policy, all with always. | Not in template. |
| Version disclosure | server_tokens off in the block and globally. | Not in template. |
| Dotfiles | location ~ /\.(?!well-known) { deny all; } | Not in template. |
| xmlrpc.php | Denied. | Denied. |
| PHP under wp-content/uploads | Denied. | Not in template. |
| Log files under the docroot | *.log, rotated and gzipped variants denied. | Not in template. |
| Bad-bot blocking | Optional per site: a user-agent map for five crawlers returns 403. | Not in template. |
| Origin lock | Optional per site: 22 Cloudflare ranges via set_real_ip_from, real IP from CF-Connecting-IP, 403 for anything else except ACME and the host itself. | Not in template. |
| Login throttling | Optional per site: limit_req zone=aurapanel_login burst=8 nodelay against a global 2 r/s zone keyed on host + IP for login paths. Off on the measured site. | Not in template. |
| WordPress multisite rewrites | The standard three-line subdirectory rewrite set inside if (!-e $request_filename). | The same three lines, plus the same two asset rewrites. Both descend from the WordPress community nginx recipe. |
| Front controller | try_files $uri $uri/ /index.php?$args in location /. | The same try_files at server level, plus if (-f $request_filename) { break; }. |
| Static assets | 17 extensions; expires max, access_log off, Cache-Control "public, immutable". | 22 extensions (adds gz, svgz, eot, ogg, ogv, zip, swf); expires max, access_log off, Access-Control-Allow-Origin "*". |
| PHP-FPM connection | Unix socket per site: unix:/run/php-fpm/example.com.sock. | TCP loopback port per site: 127.0.0.1:{{php_fpm_port}}. |
| PHP settings | Written into the site's PHP-FPM pool as php_admin_value (not in nginx); the application cannot override them. | Passed from nginx as fastcgi_param PHP_VALUE "{{php_settings}}". |
| FastCGI timeouts | fastcgi_read_timeout 300s. | fastcgi_read_timeout 3600; fastcgi_send_timeout 3600. |
| PATH_INFO | fastcgi_split_path_info and PATH_INFO set; fastcgi_intercept_errors not set. | fastcgi_intercept_errors on; no PATH_INFO. |
| Upload size | client_max_body_size from the site's PHP upload setting (64m here). | Not in template. |
| Page cache | Optional nginx FastCGI cache with WordPress-aware bypass rules, rendered into the block when enabled. | Not in nginx: CloudPanel offers Varnish Cache as a separate layer, with a WordPress plugin. |
| Extension points | Panel-managed include of a per-site redirects directory; operator drop-ins appended by the panel; a vhost editor in the panel. | A Vhost Editor that edits the template text directly. |
| Compression and modules | gzip globally; no Brotli, no PageSpeed. | nginx with the PageSpeed module per the technology stack page; compression settings not in template. |
Reading it
- Shared ancestry. The multisite rewrites, the asset rewrites and the front controller are the same lines in both. Anyone who has hand-written a WordPress server block will recognise either file.
- auraPanel puts more hardening in the block itself: security headers, dotfile and log denial, PHP-in-uploads denial, an ACME location that does not depend on the document root, and optional bot, origin-lock and login-throttling layers. Some of these may exist in CloudPanel's global configuration; they are not in the WordPress template.
- CloudPanel is ahead on transport features: HTTP/3 is documented as available, and the PageSpeed module and Varnish are part of its stack. auraPanel's nginx carries the HTTP/3 module but the panel does not switch it on — a gap this study makes visible.
- Two design choices differ rather than rank. Unix sockets versus TCP ports for PHP-FPM, and PHP settings in the pool versus in nginx via
PHP_VALUE. Sockets avoid the loopback stack; ports make the pool reachable from elsewhere. Pool-levelphp_admin_valuecannot be overridden by the application;PHP_VALUEcan be, which is sometimes wanted. - Two details worth checking in CloudPanel's template: the HTTPS redirect uses
$uri, which drops the query string, andAccess-Control-Allow-Origin "*"is set on every static asset type, which is convenient for fonts across subdomains and broader than most sites need. - The FastCGI read timeout is 300 seconds in auraPanel and 3600 in CloudPanel. Longer tolerates slow admin actions; shorter frees workers from stuck requests sooner. Neither is wrong; they are different bets.
Limitations
- One rendered file from one site with a particular set of options; CloudPanel's template with no options applied. Rendered-to-rendered on two fresh servers would be the fairer comparison, and is planned when test servers are available.
- CloudPanel's global
nginx.confwas not reviewed; "not in template" is not "absent from CloudPanel". - The CloudPanel template was last changed on 14 January 2026 and read on 17 September 2026; it may have changed since. The link goes to the live file.
- Configuration says nothing about throughput or latency. No requests were timed.
- The reviewer works on auraPanel. Every line quoted from CloudPanel's template is verbatim so that the reading can be checked.
Reproduce it
# auraPanel: the file nginx loads for a site
sudo cat /etc/nginx/sites-available/<domain>.conf
sudo /usr/sbin/nginx -V 2>&1 | tr ' ' '\n' | grep -E 'http_v3|http_v2|brotli|pagespeed'
grep -nE 'gzip|limit_req_zone|ssl_session' /etc/nginx/nginx.conf
# CloudPanel: the published template
curl -sL https://raw.githubusercontent.com/cloudpanel-io/vhost-templates/master/v2/WordPress/WordPress