Fix for changing the upstream Host from $host to $forward_host

($http_host) forwards the raw client Host header, including the port and
any absolute-form URI mismatch, to every upstream and into the asset
cache key
This commit is contained in:
Jamie Curnow committed 2026-11-09 22:09:34 +10:00
1 parent 99678969b3
commit fff2b0d097
3 files changed
+16 -6

No files matched your search

+1
View File
@@ -9,4 +9,5 @@ test/node_modules
docker/dev/dnsrouter-config.json.tmp docker/dev/dnsrouter-config.json.tmp
docker/dev/resolv.conf docker/dev/resolv.conf
.claude .claude
test/__pycache__
+10 -4
View File
@@ -52,10 +52,16 @@ http {
# Dynamically generated resolvers file # Dynamically generated resolvers file
include /etc/nginx/conf.d/include/resolvers[.]conf; include /etc/nginx/conf.d/include/resolvers[.]conf;
# Preserve the client authority, falling back when a request has no Host header. # Forwarded Host: nginx's normalised $host (lowercased, taken from the request line for
map $http_host $forward_host { # absolute-form requests) plus the client's port, so upstreams that compare Host with
default $http_host; # Origin/Referer on non-standard ports keep working. The port is only carried over when
"" $host; # the Host header names the same host nginx routed on; otherwise (no Host, absolute-form
# URI naming a different host) it falls back to bare $host. Using $host rather than the
# raw header keeps case variants and mismatched Host values out of upstream requests and
# the asset cache key.
map "$host|$http_host" $forward_host {
"~*^(?<fwd_name>[^|]+)\|\k<fwd_name>(?<fwd_port>:[0-9]+)$" "$host$fwd_port";
default $host;
} }
# Default upstream scheme # Default upstream scheme
+5 -2
View File
@@ -178,7 +178,10 @@ def check_image(image):
for authority in authorities: for authority in authorities:
for prefix in ("/", "/custom/"): for prefix in ("/", "/custom/"):
for method in ("GET", "POST"): for method in ("GET", "POST"):
origin = f"{scheme}://{authority}" # nginx forwards its normalised (lowercased) $host, as browsers send
# in Host and Origin anyway
forwarded = authority.lower()
origin = f"{scheme}://{forwarded}"
referer = origin + "/settings" referer = origin + "/settings"
path = prefix + "apply?one=1&two=2" path = prefix + "apply?one=1&two=2"
payload = "rule=example" if method == "POST" else None payload = "rule=example" if method == "POST" else None
@@ -190,7 +193,7 @@ def check_image(image):
raise AssertionError((authority, path, method, str(error))) from error raise AssertionError((authority, path, method, str(error))) from error
assert status == 200 and headers.get("Content-Type") == "application/json", ( assert status == 200 and headers.get("Content-Type") == "application/json", (
authority, path, method, status, body) authority, path, method, status, body)
assert json.loads(body) == {"host": authority, "origin": origin, assert json.loads(body) == {"host": forwarded, "origin": origin,
"referer": referer, "method": method, "path": path, "referer": referer, "method": method, "path": path,
"body": payload or ""}, body "body": payload or ""}, body
checked += 1 checked += 1