Config templates — static set, master http config, HTTP/3

The web tier is built from two distinct template surfaces, and conflating them is the single most common operator mistake:

  1. The static template set under boa-private/aegir/conf/nginx/, copied verbatim to /etc/nginx/ and /var/aegir/config/ on install and every barracuda upgrade. Plain Nginx syntax, no render step.
  2. The Provision-rendered master http{} config, generated by server.tpl.php into /var/aegir/config/ when a platform or site is verified. This is where the log format, the rate-limit zones, the realip plumbing and every map/geo actually live — none of them are in the static set.

If you go looking for BOA's rate limits or AI maps in aegir/conf/nginx/*.conf, you will not find them. Read the rendered master config and Edge policy instead.

The static template set

Files under aegir/conf/nginx/ and where each lands on disk:

File Lands at Purpose
nginx /etc/init.d/nginx sysvinit script for Nginx.
nginx.conf /etc/nginx/nginx.conf Top-level config: workers, mime pointer, log paths, vhost include directives.
mime.types /etc/nginx/mime.types MIME type map.
fastcgi_params.txt /etc/nginx/fastcgi_params FastCGI parameter set, pulled into per-site vhosts via include fastcgi_params;.
nginx_compact_include.conf /var/aegir/config/includes/ Shared compact-vhost body — the $is_crawler drop, the high-load include and the FPM HTTP_HOST pin — included by the SQL-admin vhosts (nginx_sql_*.conf:34), not by the regular Drupal site vhosts.
nginx_wild_ssl.conf /var/aegir/config/server_master/nginx/pre.d/ Wildcard SSL listener — carries the single listen … quic reuseport.
nginx_high_load_off.conf /data/conf/ Reduced-feature mode (returns 503) toggled by second.sh under high load — see Nginx debugging.
nginx_sql_adminer.conf server_master/nginx/vhost.d/ (rendered) Admin vhost for Adminer (DB UI).
nginx_sql_buddy.conf server_master/nginx/vhost.d/ (rendered) Admin vhost for sqlbuddy.
nginx_sql_cgp.conf server_master/nginx/vhost.d/ (rendered) Admin vhost for Collectd Graph Panel.
nginx_sql_chive.conf (retired) Admin vhost for Chive — see Discontinued features.
nginx_speed_purge.conf (legacy — not deployed) Old Speed Booster cache-purge endpoint; any deployed copy is now deleted by _nginx_clean_legacy_config().
nginx-squeeze-init (legacy — unreferenced) Old init script left in the tree; no longer deployed or removed.

After deploy, service nginx reload (or a full restart, for init-script changes) picks them up.

Customisation rule

Do not edit any file under /etc/nginx/ or the deployed server_master/nginx/ set directly — they are overwritten on every barracuda upgrade. Operator changes go through the include mechanism in Custom rewrites & location blocks. The static set is read-only reference: open it to understand a default block your custom include must dovetail with, never to modify in place.

The Provision-rendered master http config

server.tpl.php emits the master http{} config when a platform/site is verified. Two surfaces operators routinely look for "in the templates" live only here.

Log format

The main log format leads with the realip-resolved client, so log analysis reports the real visitor and not the spoofable proxy chain:

log_format main '"$remote_addr" $host [$time_local] '
                '"$request" $status $body_bytes_sent '
                '$request_length $bytes_sent "$http_referer" '
                '"$http_user_agent" $request_time "$gzip_ratio" '
                'proto="$server_protocol" '
                'alpn="$ssl_alpn_protocol" '
                'http2="$http2" '
                'xff="$proxy_add_x_forwarded_for"';

$remote_addr is the first token, so GoAccess (~h takes the first field) and scan_nginx both report the real client — on Cloudflare-fronted vhosts that is the realip-rewritten visitor IP, not the CF edge. The full X-Forwarded-For chain is preserved as a trailing xff="…" field, which GoAccess ignores along with the proto=/alpn=/http2= fields. The realip resolution this depends on is in Edge policy.

Rate-limiting zones

On modern Nginx ($nginx_is_modern) the master http config defines these global rate-limit / connection zones:

Zone Key Size / rate Purpose
limreq $binary_remote_addr 10m Per-IP connection limiting (limit_conn_zone).
search_limit $binary_remote_addr 10m, 3r/s Per-IP search rate cap.
search_flood $host 1m, 20r/s Per-vhost collective search ceiling across all source IPs — the primary defence against distributed one-request-per-IP search floods.
ai_search $ai_search_limit_key 10m, 1r/s AI search-bot soft limit.
ai_user $ai_user_limit_key 10m, 2r/s AI user-agent soft limit.
ai_utility $ai_utility_limit_key 5m, 1r/s AI utility-bot soft limit.
boa_i18n_anon $boa_i18n_anon_key 10m Per-vhost cap on in-flight anonymous localized (i18n) requests (limit_conn_zone, server.tpl.php:120).

The three ai_* zones key on empty-string maps that match only the relevant AI class, so no ordinary traffic is ever throttled by them. On pre-modern Nginx the fallback is a single limit_zone limreq $binary_remote_addr 10m;.

boa_i18n_anon keys on a value that is constant per vhost ($host) and non-empty only when three maps all agree: $boa_i18n_guard (default 1 = on, per-host opt-out via the wildcard-included /data/conf/boa_i18n_guard.map, server.tpl.php:924-926), $boa_i18n_path ($request_uri with a leading 2-letter language prefix, optional script/region suffix like /pt-br/, or the D7 ?q=<lang>/ form, :937-941) and $boa_is_anon (reuses the $cache_uid session map, so logged-in users are never capped, :945-948; combining map :952-955). It is enforced at location = /index.php (vhost_include.tpl.php:1735) via limit_conn boa_i18n_anon 24 — the cap is the per-platform provision option nginx_i18n_anon_conn, default 24 (vhost_include.tpl.php:1756-1761) — with limit_conn_status 444 (:1762). Detection, tuning and the opt-out procedure are owned by Request guards. The four shared http-block map variables ($boa_i18n_guard, $boa_i18n_path, $boa_is_anon, $boa_i18n_anon_key) fall under the never-remove/rename rule for map variables a deployed vhost may use — see Edge policy.

limreq and search_limit shipped in BOA 5.9.3; search_flood and the ai_* zones were added in 5.10.1; the boa_i18n_anon limit_conn zone in 5.10.3. The maps and per-vhost limit_req / if guards that consume these zones are detailed in Edge policy.

HTTP/3 + KTLS

HTTP/3 (QUIC) and KTLS (kernel TLS offload) are enabled in the BOA Nginx config. They activate automatically on barracuda upgrade once the host's Nginx build supports them; nothing is added on the configuration side.

Listener placement — reuseport is wildcard-only

The reuseport flag belongs to the single wildcard SSL listener in nginx_wild_ssl.conf (listen 127.0.0.1:443 quic reuseport;). Only one listener per addr:port may carry reuseport, so the per-site rendered SSL listeners use a plain listen *:<port> quic; without it (vhost_ssl.tpl.php:66 and :163). The listen IP is the wildcard *, not a concrete site IP ($ssl_listen_ipv4 = "*", vhost_ssl.tpl.php:48) — that is what lets any active IP on the host serve the vhost's cert. Do not expect — or add — reuseport to a rendered per-site vhost; that is the wildcard listener's job.

Picking up HTTP/3 after a host upgrade

When an upgrade brings HTTP/3 in, re-emit the vhosts so the new listener directives land per site. Verify every platform, then every site:

# As the Aegir user, per platform and per site:
drush @<alias> provision-verify

Trusted-host (HTTP_HOST) fix

Under HTTP/3 the request authority arrives in the :authority pseudo-header, not a Host: header, so Nginx left Host empty and handed Drupal an empty HTTP_HOST — breaking trusted-host checks, absolute-URL generation and redirects on HTTP/3 connections. BOA sets an explicit fastcgi_param HTTP_HOST $host on the request paths that proxy to PHP-FPM: the standard vhost path (vhost.tpl.php:63,118), the SSL path (vhost_ssl.tpl.php:122), and the ESI-microcache / update.php / authorize.php internal handlers (vhost_include.tpl.php:1574,1903,1923). The subdir handler already carried its own HTTP_HOST and is what this change matched. $host carries the correct value under HTTP/1.1, HTTP/2 and HTTP/3 alike, so Drupal always receives a populated HTTP_HOST. These fastcgi_param lines are in the Provision-rendered per-site vhosts, not the static set.

Cloudflare realip applied at install

cloudflare_realip.sh runs once during the barracuda upgrade chain — not only from the daily cron — so Cloudflare-fronted vhosts resolve the real visitor IP immediately after an upgrade, before the next cron tick. The realip directives themselves are in the master http config; see Edge policy.

Confirming HTTP/3 is active

curl --http3 -I https://yoursite.example.com/

Look for HTTP/3 200. In a browser, the protocol shows as h3 once the client has switched — typically the second page load, after the Alt-Svc header advertised HTTP/3.

SQL admin vhosts

The nginx_sql_*.conf files are dedicated subdomain server blocks, not path routes under the Octopus URL. xtra.sh.inc copies each template into ${_mtrNgx}/vhost.d/<tool>.<front> (_mtrNgx=/var/aegir/config/server_master/nginx) and rewrites the *_name server_name placeholder to a subdomain of the frontend FQDN:

Subdomain vhost Tool Xtras token Install gate
adminer.<front-fqdn> Adminer ADM (also matched by ALL) auto-installed in _EASY_SETUP=LOCAL; not auto in the default PUBLIC
sqlbuddy.<front-fqdn> sqlbuddy BDD (explicit — not in ALL) only when BDD is listed in _XTRAS_LIST
cgp.<front-fqdn> Collectd Graph Panel CGP when CGP is listed
chive.<front-fqdn> Chive — retired (legacy) see Discontinued features

The gates come from xtra.sh.inc: Adminer matches [[ "${_XTRAS_LIST}" =~ "ALL" ]] || [[ "${_XTRAS_LIST}" =~ "ADM" ]], while _if_install_sqlbuddy matches only [[ "${_XTRAS_LIST}" =~ "BDD" ]]. The ALL wildcard expands to ADM/CSF/FTP/IMG only (per barracuda.sh.cnf), so _XTRAS_LIST="ALL" does not install sqlbuddy — BDD must be listed explicitly. The default install is _EASY_SETUP=PUBLIC, under which Adminer is not auto-installed.

Protection model

Each admin vhost is protected by an IP allow/deny include plus a crawler drop — there is no Nginx basic-auth and no Drupal/Aegir SSO layer. The shipped nginx_sql_*.conf contain:

if ($is_crawler) { return 444; }
include /var/aegir/config/includes/ip_access/sqladmin*;

The sqladmin* include is generated by ip_access.sh from the validated xtras IP list (allow <ip>;deny all;). Authentication beyond the IP allowlist is the tool's own DB-credential login. The only real /sqladmin/ path route in BOA is a separate proxy vhost in aegir/tools/system/conf/ssl_proxy.conf, which uses the same ip_access include — it is not one of the nginx_sql_*.conf set, and there is no /sqlbuddy/ or /sqlchive/ path route.

Proxy vhosts speak HTTP/1.1 to origin

Since BOA 5.10.3 all five proxy templates set proxy_http_version 1.1; and clear the upstream Connection header (proxy_set_header Connection "";): aegir/tools/system/conf/proxy.conf:11-12, pln_proxy.conf:17-18, https_proxy_le.conf:19-20, ssl_proxy.conf:72-73, and the static set's nginx_wild_ssl.conf:30-31. Previously proxy_pass defaulted to HTTP/1.0 toward the origin, so origin access logs recorded proto=HTTP/1.0 for every proxied request regardless of the real client protocol — a false-positive trap for any IDS rule keyed on HTTP/1.0. This covers both the migration/PX0 proxies and the local LE proxy fronting Aegir Hostmaster and Adminer over HTTPS (the z_<domain>_ssl_proxy.conf vhost that xoct ssl-gen generates from ssl_proxy.conf). It is also the safety precondition designed to keep the HTTP/1.0 registration-spam detector free of false positives behind BOA's own proxies — see scan_nginx scoring.

Already-deployed proxies self-update; no operator action is needed: barracuda greps the deployed wildcard-SSL vhost for proxy_http_version and redeploys the template when the directive is absent (lib/functions/nginx.sh.inc:708-714), and octopus runs the same test against a deployed z_<domain>_ssl_proxy.conf, regenerating it via xoct ssl-gen (lib/functions/satellite.sh.inc:5216, condition at :5225-5230, regen at :5231-5241). The change is purely additive — no upstream keepalive pool and no WebSocket Upgrade map was added.

Reading the templates

The static set is plain Nginx config — open and read it, no build step. It is useful when debugging unexpected vhost behaviour, or when writing a custom include that must dovetail with an existing BOA block. To understand the default rate-limiting/DoS rules, do not read the static set (it contains none); read the rendered master http config and Edge policy.

Related