2

I have a flask app behind an nginx server. Nginx handles SSL termination and redirect to https. All http requests are redirected.

In flask, which I don't think should have to know about any of this, I have this bit of code:

@bp.route('/', methods=['GET', 'POST'])
def index_root():
    """Assign a session tag.

    """
    return redirect(url_for('main.index', tag=make_new_tag()))

@bp.route('/D/<tag>/accueil', methods=['GET', 'POST'])
def index(tag):
    return render_template('index.html', title='', tag=tag)

Now I have a problem: When someone requests http://example.com/, they are 301'd by nginx to https://www.example.com/, which is correct. Then flask 302's them to http://www.example.com/D/123/accueil, which is less good, because nginx will just 301 them to https://www.example.com/D/123/accueil (which is good, but I'd have preferred to skip that extra redirect).

Is this a configuration issue somewhere?

Note that in dev it's important that flask really not think about https. Indeed, flask shouldn't need to know anythint at all about https, and this is why I'm finding this a bit mystifying.

Thanks for any pointers.

jma
  • 3,580
  • 6
  • 40
  • 60
  • 1
    This Reverse Proxy [answer](https://stackoverflow.com/questions/14810795/flask-url-for-generating-http-url-instead-of-https) by @aldel might help – PGHE Nov 11 '19 at 00:05

2 Answers2

2

The current way to achieve this appears to be passing x_proto=1 as an argument to Werkzeug's ProxyFix middleware:

from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)

app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1 ,x_proto=1)

Be sure to set x_for correctly, as per the docs. This is set to the number of proxies, and must be correct for security reasons, otherwise Flask may trust some headers set by an internet user as if they'd come from the reverse proxy. The danger is setting this to 1 when there's no proxy.

Then in the nginx ensure you're setting the X-Forwarded-Proto header in the location block, before your proxy_pass:

 location  / {
    proxy_set_header Host $http_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;

    proxy_pass http://localhost:9999/;
  }

I haven't tested this with nginx but was able to test directly against the dev server and gunicorn by setting that header specifically in curl:

curl -is http://localhost:5000/  -H 'X-Forwarded-Proto: https'

This returns:

HTTP/1.0 302 FOUND
Location: https://localhost:5000/test_str/accueil`

So it appears to effect url_for which doesn't require modifying individual url_for() calls in your code, which is the case with accepted answer linked by @PGHE.

v25
  • 7,096
  • 2
  • 20
  • 36
1

I've solved this problem on reverse proxy side (Nginx) by using X-Forwarded-HTTPS instead of X-Forwarded-Proto

location  / {
    proxy_set_header Host $http_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-HTTPS    on;

    proxy_pass http://localhost:9999/;
  }
Vlad
  • 417
  • 5
  • 13