Skip to content

Commit af877cf

Browse files
xujifrhansen
andcommitted
feat: Add proxy header X-Forwarded-Host
Co-authored-by: Richard Hansen <[email protected]>
1 parent 6f4f9ec commit af877cf

File tree

7 files changed

+50
-1
lines changed

7 files changed

+50
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,15 @@ By default, `nginx-proxy` forwards all incoming request headers from the client
369369
* `Proxy`: Always removed if present. This prevents attackers from using the so-called [httpoxy attack](http://httpoxy.org). There is no legitimate reason for a client to send this header, and there are many vulnerable languages / platforms (`CVE-2016-5385`, `CVE-2016-5386`, `CVE-2016-5387`, `CVE-2016-5388`, `CVE-2016-1000109`, `CVE-2016-1000110`, `CERT-VU#797896`).
370370
* `X-Real-IP`: Set to the client's IP address.
371371
* `X-Forwarded-For`: The client's IP address is appended to the value provided by the client. (If the client did not provide this header, it is set to the client's IP address.)
372+
* `X-Forwarded-Host`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to the value of the `Host` header provided by the client. Otherwise, the header is forwarded to the backend server unmodified.
372373
* `X-Forwarded-Proto`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to `http` for plain HTTP connections and `https` for TLS connections. Otherwise, the header is forwarded to the backend server unmodified.
373374
* `X-Forwarded-Ssl`: Set to `on` if the `X-Forwarded-Proto` header sent to the backend server is `https`, otherwise set to `off`.
374375
* `X-Forwarded-Port`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to the port of the server that accepted the client's request. Otherwise, the header is forwarded to the backend server unmodified.
375376
* `X-Original-URI`: Set to the original request URI.
376377

377378
#### Trusting Downstream Proxy Headers
378379

379-
For legacy compatibility reasons, `nginx-proxy` forwards any client-supplied `X-Forwarded-Proto` (which affects the value of `X-Forwarded-Ssl`) and `X-Forwarded-Port` headers unchecked and unmodified. To prevent malicious clients from spoofing the protocol or port that is perceived by your backend server, you are encouraged to set the `TRUST_DOWNSTREAM_PROXY` value to `false` if:
380+
For legacy compatibility reasons, `nginx-proxy` forwards any client-supplied `X-Forwarded-Proto` (which affects the value of `X-Forwarded-Ssl`), `X-Forwarded-Host`, and `X-Forwarded-Port` headers unchecked and unmodified. To prevent malicious clients from spoofing the protocol, hostname, or port that is perceived by your backend server, you are encouraged to set the `TRUST_DOWNSTREAM_PROXY` value to `false` if:
380381

381382
* you do not operate a second reverse proxy downstream of `nginx-proxy`, or
382383
* you do operate a second reverse proxy downstream of `nginx-proxy` but that proxy forwards those headers unchecked from untrusted clients.
@@ -400,6 +401,7 @@ proxy_set_header Upgrade $http_upgrade;
400401
proxy_set_header Connection $proxy_connection;
401402
proxy_set_header X-Real-IP $remote_addr;
402403
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
404+
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
403405
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
404406
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
405407
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;

nginx.tmpl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
155155
'' $scheme;
156156
}
157157

158+
map $http_x_forwarded_host $proxy_x_forwarded_host {
159+
default {{ if $trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }};
160+
'' $http_host;
161+
}
162+
158163
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
159164
# server port the client connected to
160165
map $http_x_forwarded_port $proxy_x_forwarded_port {
@@ -212,6 +217,7 @@ proxy_set_header Upgrade $http_upgrade;
212217
proxy_set_header Connection $proxy_connection;
213218
proxy_set_header X-Real-IP $remote_addr;
214219
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
220+
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
215221
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
216222
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
217223
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;

test/test_headers/test_http.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy):
3030
assert "X-Forwarded-Proto: f00\n" in r.text
3131

3232

33+
##### Testing the handling of X-Forwarded-Host #####
34+
35+
def test_X_Forwarded_Host_is_generated(docker_compose, nginxproxy):
36+
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
37+
assert r.status_code == 200
38+
assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text
39+
40+
def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy):
41+
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'})
42+
assert r.status_code == 200
43+
assert "X-Forwarded-Host: example.com\n" in r.text
44+
45+
3346
##### Testing the handling of X-Forwarded-Port #####
3447

3548
def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy):

test/test_headers/test_https.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy):
3333
assert "X-Forwarded-Proto: f00\n" in r.text
3434

3535

36+
##### Testing the handling of X-Forwarded-Host #####
37+
38+
def test_X_Forwarded_Host_is_generated(docker_compose, nginxproxy):
39+
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
40+
assert r.status_code == 200
41+
assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text
42+
43+
def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy):
44+
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'})
45+
assert r.status_code == 200
46+
assert "X-Forwarded-Host: example.com\n" in r.text
47+
48+
3649
##### Testing the handling of X-Forwarded-Port #####
3750

3851
def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy):

test/test_trust-downstream-proxy/test_default.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'),
99
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'),
1010
11+
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'),
12+
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'),
13+
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'),
14+
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'),
15+
1116
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'),
1217
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'),
1318
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'),

test/test_trust-downstream-proxy/test_disabled.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'),
99
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'https'),
1010
11+
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'),
12+
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'web.nginx-proxy.tld'),
13+
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'),
14+
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'web.nginx-proxy.tld'),
15+
1116
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'),
1217
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '80'),
1318
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'),

test/test_trust-downstream-proxy/test_enabled.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'),
99
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'),
1010
11+
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'),
12+
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'),
13+
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'),
14+
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'),
15+
1116
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'),
1217
('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'),
1318
('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'),

0 commit comments

Comments
 (0)