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:
- The static template set under
boa-private/aegir/conf/nginx/, copied verbatim to/etc/nginx/and/var/aegir/config/on install and everybarracuda upgrade. Plain Nginx syntax, no render step. - The Provision-rendered master
http{}config, generated byserver.tpl.phpinto/var/aegir/config/when a platform or site is verified. This is where the log format, the rate-limit zones, the realip plumbing and everymap/geoactually 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
- Custom rewrites & location blocks — layer operator config on top of these templates.
- Edge policy — realip, the AI/abuse maps, and the rate-limit zones in the Provision master http config.
- SSL operations — the SSL workflow that uses
nginx_wild_ssl.conf. - Nginx debugging — the high-load 503 mode and 502/504 diagnosis.
- Reference appendix — consolidated template, variable, and command tables.