[Pkg-nginx-maintainers] Bug#1126960: Bug#1126960: nginx: proxy_params should use $host instead of $http_host

Gabriel Corona gabriel.corona at free.fr
Fri Feb 27 08:54:23 GMT 2026


In addition to the issue discussed by OP, using $http_host could
possibly introduce vulnerabilities to HTTP Host ambiguity attacks.

Debian's /etc/nginx/proxy_param uses the following line:

   proxy_set_header Host $http_host;

This configuration is not consistent which what is recommended by
NGINX documentation [1]:

   proxy_set_header Host $host;

Note that the nginx-snippets package contains a
common-proxy-pass-headers.conf file which includes a correct
configuration:

   proxy_set_header Host         $host;
   proxy_set_header X-Forwarded-Host   $host;

An attacker can use a HTTP/1 request with ambiguous hosts:

     GET http://host1/ HTTP/1.1
     User-Agent: UA
     Host: host2

This type of request is accepted by NGINX when using HTTP/1 only.

In this case, with "proxy_set_header Host $http_host;":

* server name "host1" is used for virtual host dispatching in NGINX;
* server name "host2" is passed to upstream HTTP server.

If NGINX is used to apply security restrictions/filtering to a
multi-host/multi-tenant backend application, this could be used to apply
the NGINX rules of "host1" while targeting upstream "host2.

For example:

* if NGINX is applying some IP adresse restriction to host2,
   the attacker can target host2 without these restrictions [a];
* if host1 and host2 are using different mTLS configuration,
   the attackers could use their mTLS keypair for host1 and
   target host2;
* if the upstream server uses the host/authority (probably a bad
   idea) to build absolute URLs, an attacker could use
   host-ambiguous requests to generate links to malicious servers
   (especially with cache poisoning).

I reached to NGINX to discuss about this potential host ambiguity and
I suggested using a "safer" logic such as:

* overriding the Host header with the request line authority if present
   (same as Apacher httpd, Traefik and Caddy);
* rejecting host-ambiguous requests entirely (same as NGINX in HTTP/2,
   HA Proxy).

Their assessment is that this is a configuration error i.e. you
should use $host and not $http_host.

More generally, other usages of $http_host introduce a host ambiguity
issue which might have a security impact (eg. for in proxy_pass,
access_log, etc.).

[a]:

   server {
       listen 443 ssl;
       server_name host1;
       ssl_certificate           /etc/nginx/ssl/host1.crt;
       ssl_certificate_key       /etc/nginx/ssl/host1.key;
       location / {
         proxy_pass http://backend;
         include proxy_params;
       }
   }
   server {
       listen 443 ssl;
       server_name host2;
       ssl_certificate           /etc/nginx/ssl/host2.crt;
       ssl_certificate_key       /etc/nginx/ssl/host2.key;
       location / {
         allow 10.0.0.0/8;
         allow 192.168.0.0/16;
         allow 127.0.0.1/8;
         deny all;
         proxy_pass http://backend;
         include proxy_params;
       }
   }

[b]:

   server {
       listen 443 ssl;
       server_name host1;
       ssl_certificate           /etc/nginx/ssl/host1.crt;
       ssl_certificate_key       /etc/nginx/ssl/host1.key;
       ssl_client_certificate    /etc/nginx/ssl/ca-host1.crt;
       ssl_verify_client         on;
       ssl_verify_depth          2;
       location / {
         proxy_pass http://backend;
         include proxy_params;
       }
   }
   server {
       listen 443 ssl;
       server_name host2;
       ssl_certificate           /etc/nginx/ssl/host2.crt;
       ssl_certificate_key       /etc/nginx/ssl/host2.key;
       ssl_client_certificate    /etc/nginx/ssl/ca-host1.crt;
       ssl_verify_client         on;
       ssl_verify_depth          2;
       location / {
         proxy_pass http://backend;
         include proxy_params;
       }
   }

=== Potential fixes/mitigation

Mitigation 0: use the hardcoded expected hostname instead of `$http_host`.

proxy_set_header Host             "www.example.com";
proxy_set_header X-Forwarded-Host "www.example.com";

Downside: not suitable for NGINX proxy_params.

Mitigation 1: use $host instead of $http_host:

   proxy_set_header Host             $host;
   proxy_set_header X-Forwarded-Host $host;

Downside: this not not include the port information which could
be a breaking change for some applications.

Mitigation 1: use $host$is_request_port$request_port instead of $http_host:

   proxy_set_header Host             $host$is_request_port$request_port;
   proxy_set_header X-Forwarded-Host $host$is_request_port$request_port;

Downside: only available since NGINX 1.29.3 (not in trixie).

Note: I think the $request_port could be spoofed anyway.

Mitigation 2: use NGINX directive to reject ambiguous (malicious) requests.

Either (does not work when using ports):

   if ($host != $http_host) {
       return 421 "Ambiguous host";
   }

or:

   if ($http_host != "$host$is_request_port$request_port") {
       return 421 "Ambiguous host";
   }

=== Q: Should NGINX accept or rejects HTTP requests with host ambiguity?

Host-ambiguous requests are accepted by NGINX based on RFC 9112 section 
3.2.2 [2]:

 > When an origin server receives a request with an absolute-form of
 > request-target, the origin server MUST ignore the received Host
 > header field (if any) and instead use the host information of
 > the request-target. Note that if the request-target does not
 > have an authority component, an empty Host header field
 > will be sent in this case.

NGINX is acting as a reverse proxy and is therefore [3] considered
an origin server in this context.

On the other hand, I would argue that rejecting the request would make
sense as per section 3.2 [4]:

 > A client MUST send a Host header field (Section 7.2 of [HTTP])
 > in all HTTP/1.1 request messages. If the target URI includes an
 > authority component, then a client MUST send a field value for Host
 > that is identical to that authority component, excluding any
 > userinfo subcomponent and its "@" delimiter (Section 4.2 of [HTTP]).

Other HTTP servers, do not work like this. For example:

* Apache HTTPD appears to ignore the incoming Host header field
   entirely when the authority is present in the request line;
* ditto for Traefik;
* ditto for Caddy
* HA proxy triggers a HTTP 400 when receiving a host-ambiguous request;
* ditto for uWSGI.

For HTTP/2, the expected behavior is clearly to reject the request
when both "Host" and ":authority" are used but are not consistent
as per RFC 9113 section 9.3.1 [5] (HTTP/2):

 > A server SHOULD treat a request as malformed if it contains a
 > Host header field that identifies an entity that differs
 > from the entity in the ":authority" pseudo-header field.

Ditto for HTTP/3, as per RFC 9114 section 4.3.1 [6] as far as
I understand:

 > If both fields are present, they MUST contain the same value.
 >
 > [..]
 >
 > An HTTP request that omits mandatory pseudo-header fields or
 > contains invalid values for those pseudo-header fields is
 > malformed.

[1] https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
[2] https://www.rfc-editor.org/rfc/rfc9112.html#name-absolute-form
[3] https://www.rfc-editor.org/rfc/rfc9110.html#name-intermediaries
[4] https://www.rfc-editor.org/rfc/rfc9112.html#section-3.2
[5] https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1
[6] https://www.rfc-editor.org/rfc/rfc9114.html#section-4.3.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 840 bytes
Desc: OpenPGP digital signature
URL: <http://alioth-lists.debian.net/pipermail/pkg-nginx-maintainers/attachments/20260227/012e81b7/attachment.sig>


More information about the Pkg-nginx-maintainers mailing list