Fix nginx emerg "unknown serve_clash_yaml variable"

The Clash maps ($is_clash_ua, $serve_clash_yaml) were defined only inside
the panel vhost file, but snippets/includes.conf consumes $serve_clash_yaml
and is included by BOTH the panel and reality vhosts. Any state where the
reality vhost loaded while the panel file did not left the variable
undefined globally -> "[emerg] unknown \"serve_clash_yaml\" variable" and
nginx -t failed.

Move both clash maps into a standalone http-level file
(sites-available/00-maps.conf) that is always symlinked into sites-enabled,
so the shared snippet's variable is defined independently of any single
vhost. Reproduced and verified the fix (and the failure without it) with
nginx 1.30.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ivan Razin
2026-07-06 09:42:40 +03:00
co-authored by Claude Opus 4.8
parent 88466b9b21
commit e4fc8a60bb
2 changed files with 28 additions and 13 deletions
+14 -7
View File
@@ -422,13 +422,11 @@ EOF
location / { try_files \$uri \$uri/ =404; }
EOF
# Main domain vhost (TLS termination at 7443, proxy_protocol)
cat > "/etc/nginx/sites-available/${domain}" <<EOF
# Rate limiting zones (http context)
limit_req_zone \$binary_remote_addr zone=diag_api:10m rate=6r/m;
limit_req_zone \$binary_remote_addr zone=diag_page:10m rate=30r/m;
limit_conn_zone \$binary_remote_addr zone=per_ip:10m;
# HTTP-level maps. The clash maps are consumed by the shared includes.conf
# snippet, which is included by BOTH vhosts, so they live in their own
# always-loaded file — never inside a single vhost, or the other vhost's
# include would reference an undefined var ("unknown ... variable").
cat > /etc/nginx/sites-available/00-maps.conf <<EOF
# Detect Clash/Mihomo clients by User-Agent
map \$http_user_agent \$is_clash_ua {
~*(clash|clashx|clashn|mihomo|stash|surfboard) 1;
@@ -440,6 +438,14 @@ map "\$is_clash_ua:\$arg_provider" \$serve_clash_yaml {
"1:" 1;
default 0;
}
EOF
# Main domain vhost (TLS termination at 7443, proxy_protocol)
cat > "/etc/nginx/sites-available/${domain}" <<EOF
# Rate limiting zones (http context)
limit_req_zone \$binary_remote_addr zone=diag_api:10m rate=6r/m;
limit_req_zone \$binary_remote_addr zone=diag_page:10m rate=30r/m;
limit_conn_zone \$binary_remote_addr zone=per_ip:10m;
# Diagnostics access: cookie issued by the SSO bridge after panel login
map \$cookie_diag_key \$diag_auth {
@@ -675,6 +681,7 @@ EOF
# Activate configs
if [[ -f "/etc/nginx/sites-available/${domain}" ]]; then
rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default
ln -sf "/etc/nginx/sites-available/00-maps.conf" /etc/nginx/sites-enabled/
ln -sf "/etc/nginx/sites-available/${domain}" /etc/nginx/sites-enabled/
ln -sf "/etc/nginx/sites-available/${reality_domain}" /etc/nginx/sites-enabled/
ln -sf "/etc/nginx/sites-available/80.conf" /etc/nginx/sites-enabled/
+14 -6
View File
@@ -336,12 +336,12 @@ cat > /etc/nginx/snippets/includes.conf <<EOF
location / { try_files \$uri \$uri/ =404; }
EOF
# ── Panel domain vhost ────────────────────────────────────────────────────────
cat > "/etc/nginx/sites-available/${domain}" <<EOF
limit_req_zone \$binary_remote_addr zone=diag_api:10m rate=6r/m;
limit_req_zone \$binary_remote_addr zone=diag_page:10m rate=30r/m;
limit_conn_zone \$binary_remote_addr zone=per_ip:10m;
# ── HTTP-level maps ───────────────────────────────────────────────────────────
# The clash maps are consumed by the shared snippets/includes.conf, which is
# included by BOTH vhosts. They must live in their own always-loaded http-level
# file: if they sat inside one vhost and that vhost was ever absent, the other
# vhost's include would reference an undefined var ("unknown ... variable").
cat > /etc/nginx/sites-available/00-maps.conf <<EOF
map \$http_user_agent \$is_clash_ua {
~*(clash|clashx|clashn|mihomo|stash|surfboard) 1;
default 0;
@@ -350,6 +350,13 @@ map "\$is_clash_ua:\$arg_provider" \$serve_clash_yaml {
"1:" 1;
default 0;
}
EOF
# ── Panel domain vhost ────────────────────────────────────────────────────────
cat > "/etc/nginx/sites-available/${domain}" <<EOF
limit_req_zone \$binary_remote_addr zone=diag_api:10m rate=6r/m;
limit_req_zone \$binary_remote_addr zone=diag_page:10m rate=30r/m;
limit_conn_zone \$binary_remote_addr zone=per_ip:10m;
# Diagnostics access: cookie issued by the SSO bridge after panel login
map \$cookie_diag_key \$diag_auth {
@@ -564,6 +571,7 @@ EOF
# ── activate sites ────────────────────────────────────────────────────────────
rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default
ln -sf "/etc/nginx/sites-available/00-maps.conf" /etc/nginx/sites-enabled/
ln -sf "/etc/nginx/sites-available/${domain}" /etc/nginx/sites-enabled/
ln -sf "/etc/nginx/sites-available/${reality_domain}" /etc/nginx/sites-enabled/
ln -sf "/etc/nginx/sites-available/80.conf" /etc/nginx/sites-enabled/