#26037 closed Cleanup/optimization (fixed)
Document precedence of USE_X_FORWARDED_HOST and USE_X_FORWARDED_PORT settings
Reported by: | Benoît Bryon | Owned by: | nobody |
---|---|---|---|
Component: | Documentation | Version: | 1.9 |
Severity: | Normal | Keywords: | |
Cc: | Matt Robenolt | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Situation is Django running behind a reverse proxy such as:
- Django settings declare
USE_X_FORWARDED_HOST = True
andUSE_X_FORWARDED_PORT = True
- reverse proxy passes headers
X-Forwarded-Host
andX-Forwarded-Port
. Say host "example.com" and port "8080" for example.
I was expecting request.get_absolute_uri()
to use both forwarded host and port.
Or more precisely, I was expecting request.get_host()
to return "example.com:8080" with the example above.
But I get "example.com" only, without mention of the forwarded port.
As of Django version 1.9, it seems that, given settings.USE_X_FORWARDED_HOST
is True, then request.get_host()
takes only X-Forwarded-Host
into account and ignores X-Forwarded-Port
.
I guess issue comes from HttpRequest._raw_host()
which doesn't use HttpRequest.get_port()
in the case settings.USE_X_FORWARDED_HOST
is True.
References:
HttpRequest.get_host()
: https://github.com/django/django/blob/b0c56b895fd2694d7f5d4595bdbbc41916607f45/django/http/request.py#L72-L89HttpRequest.get_port()
: https://github.com/django/django/blob/b0c56b895fd2694d7f5d4595bdbbc41916607f45/django/http/request.py#L110-L116settings.USE_X_FORWARDED_PORT
was introduced by https://code.djangoproject.com/ticket/25211
Change History (11)
comment:1 by , 9 years ago
Summary: | HttpRequest._get_raw_host() uses either HTTP_X_FORWARDED_HOST or HTTP_X_FORWARDED_PORT => should use both → HttpRequest.get_host() uses either HTTP_X_FORWARDED_HOST or HTTP_X_FORWARDED_PORT => should use both |
---|
comment:2 by , 9 years ago
comment:3 by , 9 years ago
Cc: | added |
---|---|
Component: | Uncategorized → HTTP handling |
Matt, any feedback on this?
comment:4 by , 9 years ago
Hm, this is a bit tricky.
The HTTP_HOST header typically includes the port number as well, so I'd expect X-Forwarded-Host to include this information.
So in my opinion, X-Forwarded-Host would supersede the use of X-Forwarded-Port.
But in the real world, I've never actually seen this.
If I were doing this in nginx, I'd have a rule like:
proxy_set_header X-Forwarded-Host $host;
And this would give you the value of the HTTP_HOST header from the client, which would already include the port number since this is what's sent along from clients.
I guess the argument could be made that the value of X-Forwarded-Port, in theory, could be used to override this? For the case of multiple proxies or something.
But again, I've never seen this case in practice.
I'd vote to just document that X-Forwarded-Host takes priority over X-Forwarded-Port imo. If someone needs some other crazy logic, it's not hard to implement your own. I see this most often done in middlewares anyways to mutate the META dict to coerce everything to behave as they want.
Now technically, in theory, you can construct an HTTP request that doesn't include the port number in the HTTP_HOST, but this is going against the spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23
Given that the spec requires this, I think it's fair to assume that X-Forwarded-Host should as well contain the port number.
Overall, this is only a problem if the port specified in X-Forwarded-Host differed from what was passed through to X-Forwarded-Port, and in which case, I can't think of when that would happen.
So I'm open to hearing a scenario where this may happen.
comment:5 by , 9 years ago
+1 for documentation tells "X-Forwarded-Host takes priority over X-Forwarded-Port". This would have saved me from trying to setup both USE_X_FORWARDED_PORT and USE_X_FORWARDED_HOST.
+1 for documentation tells "X-Forwarded-Host may include port too", with a mention to http://tools.ietf.org/html/rfc7239#page-7 or http://tools.ietf.org/html/rfc7230#section-5.4.
I updated my setup...
- nginx with
proxy_set_header X-Forwarded-Host $host:$server_port;
(removedx-forwarded-port
) settings.USE_X_FORWARDED_HOST = True
(removedUSE_X_FORWARDED_PORT
=> False by default)
... and it just works as expected.
comment:6 by , 9 years ago
Component: | HTTP handling → Documentation |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
comment:7 by , 9 years ago
Would there be any value in adding a system check for certain invalid settings configurations?
comment:8 by , 9 years ago
Has patch: | set |
---|
Meanwhile, a documentation PR has also been proposed. I'm not sure if the explanation is sufficient. Perhaps Benoit and Matt have some input.
comment:9 by , 9 years ago
Summary: | HttpRequest.get_host() uses either HTTP_X_FORWARDED_HOST or HTTP_X_FORWARDED_PORT => should use both → Document precedence of USE_X_FORWARDED_HOST and USE_X_FORWARDED_PORT settings |
---|---|
Triage Stage: | Accepted → Ready for checkin |
In the case
X_FORWARDED_PORT
is considered a edge-case and not a standard (see discussion at https://code.djangoproject.com/ticket/19517), an alternative to resolve the use case would be mentioning both domain and port in X_FORWARDED_HOST header, i.e. don't use X_FORWARDED_PORT at all.Example:
USE_X_FORWARDED_HOST = True
X_FORWARDED_HOST = "example.com:8080"
request.get_host()
returnsexample.com:8080
If this approach is preferred, it would be nice to mention it in the documentation, so that users know when to use settings.USE_X_FORWARDED_HOST and settings.USE_X_FORWARDED_PORT. At the moment, it is not obvious that we cannot use HOST and PORT at the same time.