Config templates
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 — almost none of them are in the static set (the one exception isnginx_limit_req_zones.conf, in the table below).
If you go looking for BOA's rate limits or AI maps in
aegir/conf/nginx/*.conf, you will find only the bgp_flood /
boa_perhost_anon zone file — every other zone and every AI/abuse map
lives in the rendered master config. Read the rendered master
config, Edge policy and
AI crawler 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: user/worker_processes, events tuning, gzip and keepalive defaults, the mime.types pointer and the conf.d/*.conf + sites-enabled/* include directives (no log directives — the log format and paths come from the rendered master config). |
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_limit_req_zones.conf |
/etc/nginx/conf.d/limit-req-zones-boa.conf |
The static set's one http-scope zone file: bgp_flood, boa_perhost_anon and its $boa_perhost_anon_key map — declared here rather than in the master render so per-instance vhost includes can rely on the declaration existing (lib/functions/nginx.sh.inc:808-813). |
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 |
server_master/nginx/vhost.d/ (gated) |
Admin vhost for Chive — still deployed when the CHV extra is enabled or a legacy /var/www/chive exists (lib/functions/xtra.sh.inc:110-141); 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" '
'ut="$upstream_response_time"';
$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 an xff="…" field near the end of
the line, which GoAccess ignores along with proto=/alpn=/http2=. The
actual final field is ut="$upstream_response_time" — - whenever Nginx
answered the request itself and a number only when a backend ran, so it is
the one field that separates backend cost from wall-clock (quoted because
it becomes a list when a request hits more than one upstream; trailing, so
parsers that stop earlier are unaffected). 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 |
$boa_per_ip_limit_key |
10m | Per-IP connection limiting (limit_conn_zone). The key resolves to $binary_remote_addr but empties for 127.0.0.1, so requests proxied by BOA's local wild-ssl HTTPS front never share one bucket (map server.tpl.php:531-534). |
search_limit |
$boa_per_ip_limit_key |
10m, 3r/s | Per-IP search rate cap (same loopback-exempt key). |
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 localised (i18n) requests (limit_conn_zone, server.tpl.php:123). |
bgp_flood |
$host |
1m, 5r/s | Per-vhost ceiling on D7 background_process self-request launches under /bgp-start/. Declared not in the master http config but in the BOA-written /etc/nginx/conf.d/limit-req-zones-boa.conf, so the per-instance vhost include can verify the declaration exists before rendering its consumer. See Request guards. |
boa_perhost_anon |
$boa_perhost_anon_key |
10m | Per-vhost cap on in-flight anonymous page renders at location = /index.php (limit_conn, default 100, sheds 444; session cookie empties the key so editors are never counted). Declared in the same BOA-written limit-req-zones-boa.conf; tuned per instance via the provision option nginx_perhost_anon_conn. See Request guards. |
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:1074-1077.$boa_i18n_path—$request_uriwith a leading 2-letter language prefix, optional script/region suffix like/pt-br/, or the D7?q=<lang>/form,:1087-1091.$boa_is_anon— reuses the$cache_uidsession map, so logged-in users are never capped,:1095-1098; combining map:1102-1105.
It is enforced at location = /index.php
(vhost_include.tpl.php:1824) via limit_conn boa_i18n_anon 24 — the cap is
the per-instance provision option nginx_i18n_anon_conn, default 24
(vhost_include.tpl.php:1845-1850) — with limit_conn_status 444 (:1851).
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 AI zones and their per-vendor keys are detailed in
AI crawler policy; the remaining maps and
per-vhost limit_req / if guards in Edge policy.
HTTP/3, KTLS and the trusted-host fix
HTTP/3 (QUIC) and KTLS (kernel TLS offload) activate automatically on
barracuda upgrade once the host's Nginx build supports them. The server-wide
_NGINX_KTLS opt-out, where the QUIC reuseport listener lives, the HTTP/3
HTTP_HOST fix and how to pick HTTP/3 up after a host upgrade now have their
own page: HTTP/3 and KTLS.
SQL-admin vhosts
The nginx_sql_*.conf admin vhosts (Adminer, sqlbuddy, CGP) are dedicated
subdomain server blocks, gated by the xtras keywords and guarded by an
IP-allowlist include rather than basic-auth or Ægir SSO. Their install gates
and protection model are on SQL-admin vhosts.
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:16-17, 29-30pln_proxy.conf:17-18https_proxy_le.conf:23-24, 37-38ssl_proxy.conf:85-86- the static set's
nginx_wild_ssl.conf:57-58
In proxy.conf and https_proxy_le.conf the pair appears twice — once in
the dedicated ACME-challenge location, once in location /.
Previously proxy_pass defaulted to HTTP/1.0
towards 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 Ægir 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:
barracudagreps the deployed wildcard-SSL vhost forproxy_http_versionand redeploys the template when the directive is absent (lib/functions/nginx.sh.inc:914-920).octopusruns the same test against a deployedz_<domain>_ssl_proxy.conf, regenerating it viaxoct ssl-gen(lib/functions/satellite.sh.inc:5967, condition at:5985-5991, regen at:5992-6002).
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
carries only the bgp_flood/boa_perhost_anon zone file); read the
rendered master http config and
Edge policy.
Related
- HTTP/3 and KTLS — QUIC listeners, the
_NGINX_KTLSserver-wide opt-out, and the HTTP/3 trusted-host fix. - SQL-admin vhosts — the Adminer/sqlbuddy/CGP subdomain vhosts and their IP-allowlist protection model.
- 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 variable and command tables.