-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Description
Is your feature request related to a problem? Please describe.
I have a relatively complex reverse proxy topology that involves multiple Nginx Proxy Manager instances. As shown in the diagram, I have two servers—one located on the internal network and the other on the public internet—each hosting several Docker services and using Nginx Proxy Manager for reverse proxying. I have established a tunnel between the two servers to traverse the firewall. To allow public internet access to the internal services, I added the internal Nginx Proxy Manager as an upstream backend server in the public server’s NPM configuration.
However, as illustrated in the diagram below, when I enable Force SSL on both servers, a problem occurs. The public server’s Nginx Proxy Manager uses HTTP to communicate with the internal upstream (since the tunnel is already encrypted, I prefer not to add another layer of SSL encryption). This causes the internal server’s Nginx Proxy Manager, upon receiving the HTTP request, to apply its Force SSL rule and return a 301 redirect to an HTTPS address. Consequently, even though the public client is already accessing the site via HTTPS, it gets redirected to the same HTTPS URL again, triggering a browser ERR_TOO_MANY_REDIRECTS error.
Describe the solution you'd like
I developed a program capable of returning the request headers in response body, and used Nginx Proxy Manager to reverse proxy it. As shown in the screenshot below, I discovered that Nginx Proxy Manager adds the X-Forwarded-Scheme and X-Forwarded-Proto request headers. Furthermore, when a client correctly accesses a virtual host with Force SSL enabled via HTTPS, these headers already indicate that the original request was made over HTTPS.
I modified the newly introduced conf.d/include/force-ssl.conf file, which is applied when Force SSL is enabled. I added a condition to prevent the 301 redirect from being triggered when the upstream X-Forwarded-Proto header is https. The modifications are as follows:
set $test "";
if ($scheme = "http") {
set $test "H";
}
if ($request_uri = /.well-known/acme-challenge/test-challenge) {
set $test "${test}T";
}
# Added code begin
if ($http_x_forwarded_proto = "https") {
set $test "${test}S";
}
# Added code end
if ($test = H) {
return 301 https://$host$request_uri;
}
After reload nginx, this problem has been resolved.
Describe alternatives you've considered
Under my solution, the two-layer Nginx Proxy Manager proxy configuration works, but it may not be suitable for more layers. This is because I found that the X-Forwarded-Proto header received by deeper layers may be overwritten as http by the preceding Nginx Proxy Manager instance. I believe that if support for multiple Nginx Proxy Manager instances in a forwarding chain is desired, additional handling of the X-Forwarded-Proto and X-Forwarded-Scheme headers may be necessary to ensure that the original protocol used by the client is not lost during multiple forwarding hops.
In addition, I have considered the security implications. Such modifications could potentially cause the Force SSL feature to fail if clients spoof the X-Forwarded-* headers. Therefore, a more thorough design may be necessary.