Research

What a control panel writes into nginx for WordPress: auraPanel and CloudPanel side by side

Summary. Both panels ship a hardened WordPress server block with TLS 1.2+, HTTP/2, hidden version, blocked dotfiles and long-lived static caching. They differ on HTTP/3, PageSpeed and Varnish (CloudPanel), and on per-site logs, an ACME location, bot blocking, xmlrpc and uploads-PHP denial, a bad-bot map and an optional Cloudflare origin lock (auraPanel). Read from primary sources with dates.

Measured
17 September 2026
Method
rendered configuration from a live host vs the competitor's public template repository
auraPanel
1.21.3, server block rendered for a live WordPress site, read from /etc/nginx/sites-available on a Debian 13 host with nginx 1.31.2
CloudPanel
template v2/WordPress/WordPress from the public vhost-templates repository, last changed 14 January 2026

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 wordpress template 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 as example.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

ConcernauraPanel (rendered)CloudPanel (template)
Server blocksTwo: 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 versionshttp2 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 redirectreturn 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 ciphersTLS 1.2 and 1.3 in the block; server cipher preference off; session tickets off globally.Not in template.
ACME challengesServed 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 headersHSTS (1 year), X-Content-Type-Options, X-Frame-Options (per-site policy), Referrer-Policy, all with always.Not in template.
Version disclosureserver_tokens off in the block and globally.Not in template.
Dotfileslocation ~ /\.(?!well-known) { deny all; }Not in template.
xmlrpc.phpDenied.Denied.
PHP under wp-content/uploadsDenied.Not in template.
Log files under the docroot*.log, rotated and gzipped variants denied.Not in template.
Bad-bot blockingOptional per site: a user-agent map for five crawlers returns 403.Not in template.
Origin lockOptional 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 throttlingOptional 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 rewritesThe 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 controllertry_files $uri $uri/ /index.php?$args in location /.The same try_files at server level, plus if (-f $request_filename) { break; }.
Static assets17 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 connectionUnix socket per site: unix:/run/php-fpm/example.com.sock.TCP loopback port per site: 127.0.0.1:{{php_fpm_port}}.
PHP settingsWritten 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 timeoutsfastcgi_read_timeout 300s.fastcgi_read_timeout 3600; fastcgi_send_timeout 3600.
PATH_INFOfastcgi_split_path_info and PATH_INFO set; fastcgi_intercept_errors not set.fastcgi_intercept_errors on; no PATH_INFO.
Upload sizeclient_max_body_size from the site's PHP upload setting (64m here).Not in template.
Page cacheOptional 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 pointsPanel-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 modulesgzip 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-level php_admin_value cannot be overridden by the application; PHP_VALUE can be, which is sometimes wanted.
  • Two details worth checking in CloudPanel's template: the HTTPS redirect uses $uri, which drops the query string, and Access-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.conf was 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

Other studies

All research →  ·  Documentation →

Try the live demo

A real auraPanel on a real server, shared with everyone. Sign in with the demo account and click around.

Email
demo@goaura.one
Password
demo1234
Open the demo panel ↗

Shared public demo, so please don’t put anything personal in it. More about the demo →

Talk to us

Tell us what you need and how to reach you. A person reads this, not a queue.

We use these details only to reply to you. Privacy

Recent updates

What shipped in the last releases. With automatic updates on, each lands on your server the moment it is published.

  1. v1.21.3
    Framework sites were undetectable, and a restore could silently point the copy at the original's live database.

    DetectApp looked for each application's signature relative to the docroot — but every PHP framework in the registry serves from a subdirectory (DocrootSub: public, webroot, web, pub, docroot) and keeps the file that identifies it one level *above* that. So the Laravel signature searched <project>/public/artisan,…

  2. v1.21.2
    Restarting nginx now blocks the panel and reconnects, instead of showing a notice.

    v1.21.1 stopped the false "HTTP 520" but only put up a toast — and a toast is the wrong affordance when the panel itself is about to go away: the page sat there looking broken while every request failed underneath it.

  3. v1.21.1
    Restarting nginx from Services no longer reports a false failure.

    It showed "Restart failed — HTTP 520". The panel is reverse-proxied *by* nginx, so the request deadlocked against itself: the handler blocked waiting for the restart to finish, while nginx's graceful stop waited for that very request to drain. Neither could proceed until nginx's stop-retry timeout force-killed it,…

  4. v1.21.0
    Per-site X-Frame-Options, fixing page builders that couldn't load their preview.

    Elementor's editor failed with *"Multiple 'X-Frame-Options' headers with conflicting values ('DENY, SAMEORIGIN')"*. The cause is that nginx's add_header appends rather than replaces: when the application also sets the header — a WordPress security plugin, or a CDN transform — the browser received two values,…

  5. v1.20.6
    The multi-database restore refusal now says what is in the backup and what still works.

    It read only "Restoring a multi-database site isn't supported yet", which is a dead end: it named neither the databases involved, nor why one of them is the limit, nor any route forward.

  6. v1.20.5
    Services: cards in a row now match heights.

    Like the settings grid before it, the Services grid pinned each card to its own content height, so a row often showed one card noticeably shorter than the card beside it. Each row now takes the taller card's height and both fill it, and it re-balances as content changes — a runtime installed, a longer service list.

  7. v1.20.4
    Settings: paired cards now match heights.

    Organisation / Panel Domain and auraPanel Updates / Session timeout each sat at their own content height, so a row showed one card visibly shorter than its neighbour. Each row now takes the taller card's height and both fill it — driven by content, not a fixed value, so it still adapts as a card grows (a validation…

  8. v1.20.3
    Settings: Organisation and Panel Domain now share a row too.

    They were full-width blocks stacked above the rest of the page; they now sit side by side in the same two-column grid as the Updates / Session timeout pair, so the top of Settings reads as two tidy rows instead of four stacked bands. Both cards shrink cleanly — the logo drop zone is a fixed 44×44 and the name field…

All releases on GitHub ↗