6

When I POST a new resource to my RESTful Tastypie API, I create a resource and get a 201 response like this:

HTTP/1.1 201 CREATED
Content-Type: text/html; charset=utf-8
Date: Wed, 19 Sep 2012 01:02:48 GMT
Location: http://example.com/api/v1/resource/12/
Server: gunicorn/0.14.6
Content-Length: 0
Connection: keep-alive

Great! Except I posted to an HTTPS URL and would like to get a HTTPS Location header back. How can I configure tastypie to do this?

Addition

I am using some middleware to force SSL, but I don't think it is the cause of this issue. Here it is anyway:

class SSLifyMiddleware(object):
    # Derived from https://github.com/rdegges/django-sslify
    def process_request(self, request):
        if not any((not settings.FORCE_SSL, request.is_secure(), request.META.get('HTTP_X_FORWARDED_PROTO', '') == 'https')):
            url = request.build_absolute_uri(request.get_full_path())
            secure_url = url.replace('http://', 'https://')
            return HttpResponseRedirect(secure_url)

Addition

This is a Heroku app.

Erik
  • 7,479
  • 8
  • 62
  • 99
  • 3
    Is there any reason your `request.is_secure()` result would be `False`? TastyPie does several things when returning this request, but it can base only on `request.build_absolute_uri()` when building the URLs, so it goes down to the result of `request.is_secure()` when determining whether the protocol should be `http` or `https`. – Tadeck Sep 19 '12 at 04:04

1 Answers1

5

As we have determined, the reason of the URL beginning with http instead of https is request.is_secure() being False.

There are several possible reasons resulting in request.is_secure() being False, such as being behind load balancer or reverse proxy that connects to the server using HTTP while the connection between client and load balancer / reverse proxy is made using SSL.

Please take a look at the documentation of SECURE_PROXY_SSL_HEADER, which is some solution, if you are behind a proxy or load balancer:

If your Django app is behind a proxy, though, the proxy may be "swallowing" the fact that a request is HTTPS, using a non-HTTPS connection between the proxy and Django. In this case, is_secure() would always return False -- even for requests that were made via HTTPS by the end user.

In this situation, you'll want to configure your proxy to set a custom HTTP header that tells Django whether the request came in via HTTPS, and you'll want to set SECURE_PROXY_SSL_HEADER so that Django knows what header to look for.

Community
  • 1
  • 1
Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • Thanks -- I'll look at this carefully as this app is on Heroku. This may very well be the issue. – Erik Sep 19 '12 at 17:50
  • @Erik: If this is Heroku, try using the setting I referenced and add HTTP_X_FORWARDED_PROTO to the list of headers indicating secure connection. For more details check this: http://stackoverflow.com/a/9207726/548696. Indeed it looks Heroku has some kind of reverse proxy or load balancer, but I have not been using it enough myself to confirm it. – Tadeck Sep 19 '12 at 18:11
  • Got it working. Your link helped me find that I was leaving out the `SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')` from my settings that my SSLify middleware was looking for. My only concern now is verification that Heroku is stripping the header off of incoming http requests. – Erik Sep 19 '12 at 18:38
  • This was helpful too: http://stackoverflow.com/questions/8436666/how-to-make-python-on-heroku-https-only. In fact, that is where I derived my SSLify middleware from. – Erik Sep 19 '12 at 18:39
  • @Erik: I am sure they do. If they don't, they have a hole in their security (because this is the only way to distinguish secure requests from insecure ones). – Tadeck Sep 19 '12 at 18:40
  • I tested it with a non-https POST request to the resource list (/api/v1/resource/) that included the HTTP_X_FORWARDED_PROTO header and Heroku correctly provides a 302 FOUND status code with the response Location header pointing at the https resource list URI. Good stuff. Thanks for you help Tadeck. – Erik Sep 19 '12 at 18:56