Opened 8 years ago

Closed 8 years ago

Last modified 8 years ago

#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 and USE_X_FORWARDED_PORT = True
  • reverse proxy passes headers X-Forwarded-Host and X-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:

Change History (11)

comment:1 by Benoît Bryon, 8 years ago

Summary: HttpRequest._get_raw_host() uses either HTTP_X_FORWARDED_HOST or HTTP_X_FORWARDED_PORT => should use bothHttpRequest.get_host() uses either HTTP_X_FORWARDED_HOST or HTTP_X_FORWARDED_PORT => should use both

comment:2 by Benoît Bryon, 8 years ago

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:

  • Django settings declare USE_X_FORWARDED_HOST = True
  • Reverse proxy sets header X_FORWARDED_HOST = "example.com:8080"
  • request.get_host() returns example.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.

comment:3 by Tim Graham, 8 years ago

Cc: Matt Robenolt added
Component: UncategorizedHTTP handling

Matt, any feedback on this?

comment:4 by Matt Robenolt, 8 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 Benoît Bryon, 8 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; (removed x-forwarded-port)
  • settings.USE_X_FORWARDED_HOST = True (removed USE_X_FORWARDED_PORT => False by default)

... and it just works as expected.

comment:6 by Tim Graham, 8 years ago

Component: HTTP handlingDocumentation
Triage Stage: UnreviewedAccepted
Type: BugCleanup/optimization

comment:7 by Tim Graham, 8 years ago

Would there be any value in adding a system check for certain invalid settings configurations?

comment:8 by Tim Graham, 8 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 Tim Graham, 8 years ago

Summary: HttpRequest.get_host() uses either HTTP_X_FORWARDED_HOST or HTTP_X_FORWARDED_PORT => should use bothDocument precedence of USE_X_FORWARDED_HOST and USE_X_FORWARDED_PORT settings
Triage Stage: AcceptedReady for checkin

comment:10 by Tim Graham <timograham@…>, 8 years ago

Resolution: fixed
Status: newclosed

In 5cda4677:

Fixed #26037 -- Documented precedence of USE_X_FORWARDED_HOST/PORT settings.

comment:11 by Tim Graham <timograham@…>, 8 years ago

In 1920d4d:

[1.9.x] Fixed #26037 -- Documented precedence of USE_X_FORWARDED_HOST/PORT settings.

Backport of 5cda4677b3df1be971000ef27470d3efc308d3be from master

Note: See TracTickets for help on using tickets.
Back to Top