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?

+3


source to share


1 answer


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 )

+4


source







All Articles