django的request.META.get('wsgi.url_scheme')返回http而不是https

时间:2017-05-11 04:06:19

标签: python django nginx gunicorn

当我看到request.META.get('wsgi.url_scheme')返回 http 并且我的request.scheme返回 https

时,我觉得很奇怪

这是我的请求对象字典的样子:

{'session': <django.contrib.sessions.backends.cache.SessionStore object at 0x7fec7ff57a90>, '_post': <QueryDict: {u'notify_page': [u'https://example.com/my/deposit/success/'], u'bank': [u'CMB'], u'transaction_id': [u'2017051111382524291123'], u'payment_type': [u'1']}>, 'COOKIES': {'csrftoken': 'Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I', 'sessionid': 'uuoxols288i9018fw11coz1yvvwn9ndh', 'auth_req': ''}, '_post_parse_error': False, 'resolver_match': ResolverMatch(func=pay.views.perform, args=(), kwargs={}, url_name=None, app_names=[], namespaces=[]), 'user_agent': <SimpleLazyObject: <function <lambda> at 0x7fec842d2c80>>, '_stream': <_io.BytesIO object at 0x7fec84017cb0>, '_body': 'transaction_id=2017051111382524291123&notify_page=https%3A%2F%2Fexample.com%2Fmy%2Fdeposit%2Fsuccess%2F&bank=CMB&payment_type=1', '_files': <MultiValueDict: {}>, '_read_started': True, 'META': {'HTTP_REFERER': 'https://example.com/my/deposit', 'HTTP_X_FORWARDED_SSL': 'on', 'SERVER_SOFTWARE': 'gunicorn/19.7.1', 'SCRIPT_NAME': u'', 'REQUEST_METHOD': 'POST', 'PATH_INFO': u'/payment/', 'HTTP_ORIGIN': 'https://example.com', 'SERVER_PROTOCOL': 'HTTP/1.0', 'QUERY_STRING': '', 'HTTP_X_REAL_IP': '180.232.79.194', 'CONTENT_LENGTH': '127', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36', 'HTTP_CONNECTION': 'close', 'HTTP_COOKIE': 'csrftoken=Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I; auth_req=; sessionid=uuoxols288i9018fw11coz1yvvwn9ndh', 'SERVER_NAME': '0.0.0.0', 'REMOTE_ADDR': '172.17.0.2', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '8000', 'REMOTE_PORT': '57938', 'HTTP_X_FORWARDED_PROTO': 'https', 'wsgi.input': <gunicorn.http.body.Body object at 0x7fec84220750>, 'HTTP_HOST': 'example-backend.com', 'wsgi.multithread': True, 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'wsgi.version': (1, 0), 'RAW_URI': '/payment/', 'wsgi.run_once': False, 'wsgi.errors': <gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7fec84036550>, 'wsgi.multiprocess': True, 'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.8', 'gunicorn.socket': <socket at 0x7fec7ff95590 fileno=14 sock=172.17.0.6:8000 peer=172.17.0.2:57938>, 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'HTTP_X_FORWARDED_FOR': '180.232.79.194', 'wsgi.file_wrapper': <class 'gunicorn.http.wsgi.FileWrapper'>, u'CSRF_COOKIE': u'Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br'}, 'environ': {'HTTP_REFERER': 'https://example.com/my/deposit', 'HTTP_X_FORWARDED_SSL': 'on', 'SERVER_SOFTWARE': 'gunicorn/19.7.1', 'SCRIPT_NAME': u'', 'REQUEST_METHOD': 'POST', 'PATH_INFO': u'/payment/', 'HTTP_ORIGIN': 'https://example.com', 'SERVER_PROTOCOL': 'HTTP/1.0', 'QUERY_STRING': '', 'HTTP_X_REAL_IP': '180.232.79.194', 'CONTENT_LENGTH': '127', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36', 'HTTP_CONNECTION': 'close', 'HTTP_COOKIE': 'csrftoken=Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I; auth_req=; sessionid=uuoxols288i9018fw11coz1yvvwn9ndh', 'SERVER_NAME': '0.0.0.0', 'REMOTE_ADDR': '172.17.0.2', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '8000', 'REMOTE_PORT': '57938', 'HTTP_X_FORWARDED_PROTO': 'https', 'wsgi.input': <gunicorn.http.body.Body object at 0x7fec84220750>, 'HTTP_HOST': 'example-backend.com', 'wsgi.multithread': True, 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'wsgi.version': (1, 0), 'RAW_URI': '/payment/', 'wsgi.run_once': False, 'wsgi.errors': <gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7fec84036550>, 'wsgi.multiprocess': True, 'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.8', 'gunicorn.socket': <socket at 0x7fec7ff95590 fileno=14 sock=172.17.0.6:8000 peer=172.17.0.2:57938>, 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'HTTP_X_FORWARDED_FOR': '180.232.79.194', 'wsgi.file_wrapper': <class 'gunicorn.http.wsgi.FileWrapper'>, u'CSRF_COOKIE': u'Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br'}, 'path_info': u'/payment/', '_messages': <django.contrib.messages.storage.fallback.FallbackStorage object at 0x7fec7ffccf10>, 'LANGUAGE_CODE': u'en', 'path': u'/payment/', 'method': 'POST', 'user': <SimpleLazyObject: <function <lambda> at 0x7fec842d2f50>>}

这是META返回的请求

{'HTTP_REFERER': 'https://example.com/my/deposit', 'HTTP_X_FORWARDED_SSL': 'on', 'SERVER_SOFTWARE': 'gunicorn/19.7.1', 'SCRIPT_NAME': u'', 'REQUEST_METHOD': 'POST', 'PATH_INFO': u'/payment/', 'HTTP_ORIGIN': 'https://example.com', 'SERVER_PROTOCOL': 'HTTP/1.0', 'QUERY_STRING': '', 'HTTP_X_REAL_IP': '180.232.79.194', 'CONTENT_LENGTH': '127', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36', 'HTTP_CONNECTION': 'close', 'HTTP_COOKIE': 'csrftoken=Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I; auth_req=; sessionid=uuoxols288i9018fw11coz1yvvwn9ndh', 'SERVER_NAME': '0.0.0.0', 'REMOTE_ADDR': '172.17.0.2', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '8000', 'REMOTE_PORT': '57938', 'HTTP_X_FORWARDED_PROTO': 'https', 'wsgi.input': <gunicorn.http.body.Body object at 0x7fec84220750>, 'HTTP_HOST': 'example-backend.com', 'wsgi.multithread': True, 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'wsgi.version': (1, 0), 'RAW_URI': '/payment/', 'wsgi.run_once': False, 'wsgi.errors': <gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7fec84036550>, 'wsgi.multiprocess': True, 'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.8', 'gunicorn.socket': <socket at 0x7fec7ff95590 fileno=14 sock=172.17.0.6:8000 peer=172.17.0.2:57938>, 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'HTTP_X_FORWARDED_FOR': '180.232.79.194', 'wsgi.file_wrapper': <class 'gunicorn.http.wsgi.FileWrapper'>, u'CSRF_COOKIE': u'Y3FRFDfvK6U2dkgVr3KarzJE6GUdAZ1I', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br'}

我的Nginx

ssl_certificate     /etc/nginx/ssl/example.com/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.com/example.key;

server {

    listen 443 ssl;
    server_name example.com;
    charset utf-8;
    access_log /var/log/nginx/access.django.log;
    error_log /var/log/nginx/error.django.log;


    location /static {
        alias /usr/src/app/static;
    }

    location /upload { # IMPORTANT! To allow the CDN to access our files
        alias /usr/src/app/upload;
        expires 240h;  # 10 days
        add_header Cache-Control "public";  # to allow the CDN to cache our files
    }

    location / {
        proxy_pass http://django:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

我知道要解决我的问题,我只需要将所有request.META.get('wsgi.url_scheme')替换为request.scheme,但也许某人可以解释为什么会发生

1 个答案:

答案 0 :(得分:1)

短版:您应该在gunicorn设置中将您的nginx服务器地址添加到forwarded_allow_ips

长版

根据request.META值,我假设nginx和你的django应用程序部署在不同的服务器上。

因此,nginx收到HTTPS请求,将X-Forwarded-Proto标头设置为https并将请求传递给gunicorn。

由于request.scheme标题,

https被django设置为X-Forwarded-Proto,因为你的django配置中可能有这样的一行:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

wsgi.url_scheme由gunicorn设定。如果设置secure_scheme_headersforwarded_allow_ips允许,它会从同一标头(X-Forwarded-Proto)中获取值。默认情况下,X-Forwarded-Proto中包含secure_scheme_headers。但forwarded_allow_ips的默认值为127.0.0.1。所以gunicorn会忽略标题并将wsgi.url_scheme设置为http。将forwarded_allow_ips设置为nginx服务器的地址将解决问题。

forwarded_allow_ips设置相当危险。在同一台服务器上部署nginx和django时很难注意到它。然后,您转到多服务器设置,注意您的重定向和绝对URL正在使用HTTP。

如果未设置requests.scheme,则BTW wsgi.url_scheme将使用SECURE_PROXY_SSL_HEADER中的值。所以,只要SECURE_PROXY_SSL_HEADER设置正确,我就不知道你为什么需要wsgi.url_scheme

此外,您可能希望使用更简单/标准/可靠的方式检查HTTPS:HttpRequest.is_secure()