65
For the 2nd part of John C’s answer, and Django 1.4+…
Instead of extending HttpResponseRedirect, you can change the request.scheme
to https
.
Because Django is behind Nginx’s reverse proxy, it doesn’t know the original request was secure.
In your Django settings, set the SECURE_PROXY_SSL_HEADER setting:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Then, you need Nginx to set the custom header in the reverse proxy. In the Nginx site settings:
location / {
# ...
proxy_set_header X-Forwarded-Proto $scheme;
}
This way request.scheme == 'https'
and request.is_secure()
returns True.
request.build_absolute_uri()
returns https://...
and so on…
21
Here is the solution I’ve worked out so far. There are two parts, configuring nginx, and writing code for Django. The nginx part handles external requests, redirecting http
pages to https
, and the Django code handles internal URL generation that has an http
prefix. (At least, those resulting from a HttpResponseRedirect()
). Combined, it seems to work well – as far as I can tell, the client browser never sees an http
page that the users didn’t type in themselves.
Part one, nginx configuration
# nginx.conf
# Redirects any requests on port 80 (http) to https:
server {
listen 80;
server_name www.mysite.com mysite.com;
rewrite ^ https://mysite.com$request_uri? permanent;
# rewrite ^ https://mysite.com$uri permanent; # also works
}
# django pass-thru via uWSGI, only from https requests:
server {
listen 443;
ssl on;
ssl_certificate /etc/ssl/certs/mysite.com.chain.crt;
ssl_certificate_key /etc/ssl/private/mysite.com.key;
server_name mysite.com;
location / {
uwsgi_pass 127.0.0.1:8088;
include uwsgi_params;
}
}
Part two A, various secure cookie settings, from settings.py
SERVER_TYPE = "DEV"
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True # currently only in Dev branch of Django.
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
Part two B, Django code
# mysite.utilities.decorators.py
import settings
def HTTPS_Response(request, URL):
if settings.SERVER_TYPE == "DEV":
new_URL = URL
else:
absolute_URL = request.build_absolute_uri(URL)
new_URL = "https%s" % absolute_URL[4:]
return HttpResponseRedirect(new_URL)
# views.py
def show_items(request):
if request.method == 'POST':
newURL = handle_post(request)
return HTTPS_Response(request, newURL) # replaces HttpResponseRedirect()
else: # request.method == 'GET'
theForm = handle_get(request)
csrfContext = RequestContext(request, {'theForm': theForm,})
return render_to_response('item-search.html', csrfContext)
def handle_post(request):
URL = reverse('item-found') # name of view in urls.py
item = request.REQUEST.get('item')
full_URL = '%s?item=%s' % (URL, item)
return full_URL
Note that it is possible to re-write HTTPS_Response()
as a decorator. The advantage would be – not having to go through all your code and replace HttpResponseRedirect()
. The disadvantage – you’d have to put the decorator in front of HttpResponseRedirect()
, which is in Django at django.http.__init__.py
. I didn’t want to modify Django’s code, but that’s up to you – it’s certainly one option.
- [Django]-POST jQuery array to Django
- [Django]-Django Admin – Disable the 'Add' action for a specific model
- [Django]-Page not found 404 Django media files
5
if you stick your entire site behind https, you don’t need to worry about it on the django end. (assuming you don’t need to protect your data between nginx and django, only between users and your server)
- [Django]-Python NameError: name 'include' is not defined
- [Django]-Django check for any exists for a query
- [Django]-Django 1.7 throws django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet