0

I have a Flask-Restful application running on nginx with uwsgi. Some of its endpoints are supposed to return an 202 Accepted response with Location: header.

I didn't specify any protocol or hostname when I put the Location: header from my python code.

class MyEndpoint(MethodView):
  def get(self):
    ...
    return {"status": "Accepted"}, 202, {"Location": "/api/another/endpoint"}

However, when I actually try a request, it somehow puts protocol and hostname to the location header.

$ curl -I "https://api.myhost.com/api/myendpoint"
HTTP/1.1 202 ACCEPTED
Server: nginx/1.1.19
Date: Thu, 30 Jun 2016 03:18:53 GMT
Content-Type: application/json
Content-Length: 23
Connection: keep-alive
Location: http://api.myhost.com/api/another/endpoint

This is a problem because even though I have requested the first endpoint over HTTPS, but the Location: header comes with HTTP. When I try another request to the /api/another/endpoint, the browser refused to do it saying that it's an insecure XMLHttpRequest.

I don't think it's done by Flask, since I didn't put api.myhost.com in nowhere of my application settings. I suspect it's nginx's work, but I don't know how to fix it.

This is my nginx configuration.

server {
    listen 80;
    listen 443 ssl;

    server_name api.myhost.com;
    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;

    location / { try_files $uri @my_api; }
    location @my_api {
        include uwsgi_params;
        uwsgi_pass unix:/var/run/uwsgi/my-api.sock;
    }
}

All I want is for the Location: header value to echo the request's protocol and hostname, or to strip off the host part and leave the path part only.

  • Try including `SERVER_PROTOCOL $server_protocol;` to your `uwsgi_params` or explicitly to your server block. This way it would show both http or https depending on the request, since your server block is listening on both ports. P.S My advice would be to have a separate server block listening on 80 with `api.myhost.com` and then returning 301 to the 443 block. This will ensure your requests always get served over SSL, unless this was not your intention. – Keenan Lawrence Jun 30 '16 at 21:14
  • @KeenanLawrence `SERVER_PROTOCOL $server_protocol;` is already in my `uwsgi_params`. Thank you for the advice of separating server blocks, but it doesn't help with this problem. My server would properly send 301 with the request of `http://api.myhost.com/api/another/endpoint`, but the browser refuses to send that request in the first place, because of the Same Origin Policy. – Chanu Lee Jul 01 '16 at 05:30
  • I see. Yes, separating the server blocks was just a security measure. You don't want to be accidentally sending content over http when it's supposed to be served over https. With you config, it's possible that this could happen. In any case, I don't know flask so you can do this in nginx if you're okay with having a location header in **all** your responses. In the location block for `@my_api`, add this line `add_header Location $scheme$host$request_uri;` You'll have to remove the Location response from you python code though – Keenan Lawrence Jul 01 '16 at 08:39
  • It seems as if you can set your preferred scheme in the Flask config, along with the server name. Have a look [here](http://flask.pocoo.org/docs/0.11/config/) for the builtin config variables. – Keenan Lawrence Jul 01 '16 at 08:40

0 Answers0