Why is django ignoring HTTP_X_FORWARDED_PROTO from wire but not in tests?
Why is django ignoring HTTP_X_FORWARDED_PROTO if it goes through the wire?
I added the following configuration to settings.xml:
# make sure we know when we are secure when we are behind a proxy
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
I made a test to check that if
def testHttpSupport(self):
url = reverse('configuration-list')
response = self.client.get(url, HTTP_X_FORWARDED_PROTO='https')
cfg = response.data[0]
cfg_url = cfg['url']
self.assertTrue(cfg_url.startswith('https'))
this works great. The returned object URL starts with https.
however, if I try:
curl -v -H 'HTTP_X_FORWARDED_PROTO: https' http://localhost:8000/api/users/
...
> GET /api/users/ HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.51.0
> Accept: */*
> HTTP_X_FORWARDED_PROTO: https
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Mon, 03 Jul 2017 16:22:04 GMT
< Server: WSGIServer/0.2 CPython/3.6.1
< Content-Type: application/json
< Allow: GET, POST, OPTIONS
< Vary: Accept, Cookie
< X-Frame-Options: SAMEORIGIN
< Content-Length: 197
<
* Curl_http_done: called premature == 0
* Closing connection 0
[{"url":"http://localhost:8000/api/users/1/",...
Why doesn't this return URLs based on https: // 'like in my unit test?
source to share
The problem is the title name. When accessing Django through a WSGI server, you must use a header X-Forwarded-Proto
instead HTTP_X_FORWARDED_PROTO
:
curl -v -H 'X-Forwarded-Proto: https' http://localhost:8000/api/users/
The WSGI protocol states that the relevant CGI specifications must be followed, which says:
Meta variables with names starting with "HTTP_" contain the values ββread from the client request header fields if the protocol being used is HTTP. The HTTP header field name is converted to uppercase, has all occurrences of "-" replaced with "_", and has "HTTP_" appended to the enter meta variable name.
( source )
So whenever you use a WSGI server, the header is X-Forwarded-Proto
automatically converted to HTTP_X_FORWARDED_PROTO
before being passed to Django. When you go to the header HTTP_X_FORWARDED_PROTO
instead, HTTP_
it should still be added as per the spec. This way you end up with a named title HTTP_HTTP_X_FORWARDED_PROTO
in Django.
self.client
is not a WSGI server, and the values ββpassed through kwarg are inserted directly into the WSGI environment without any processing. So in this case you need to do the conversion yourself and actually use the key HTTP_X_FORWARDED_PROTO
:
CGI specification
Headers sent via ** optional must comply with the CGI specification. For example, emulating a different "Host" header passed in an HTTP request from the browser to the server should be passed as HTTP_HOST.
( source )
source to share