Flask: url_for + redirect does not respect http vs https

Created on 18 Jun 2013  路  11Comments  路  Source: pallets/flask

I am using Flask with SSL.
Testing in dev environment with stunnel and a self-signed certificate.

http://localhost:5000 is Flask
https://localhost:8000 is SSL Flask through stunnel

When using redirect(url_for(...)) in the program below, the browser is redirected to http://localhost:8000/ssl (HTTP protocol instead of HTTPS)

Correct URL should be https://localhost:8000/ssl

Here is a sample program to demonstrate:

from flask import Flask, request, url_for, redirect

# Flask Initialization
app = Flask(__name__)

@app.route("/")
def index():
    return "Hello World"

@app.route("/ssl")
def ssl():
    return "Hello SSL World"

@app.route('/redirect/')
def redirectToSSL():
    return redirect(url_for('ssl'))

if __name__ == "__main__":
    app.run(debug=True)

My stunnel config is

pid =
foreground = yes

cert = ./stunnel.crt
key = ./stunnel.pem

[https]
accept=8000
connect=5000

To test this,

  • start your stunnel
  • start the application
  • connect to https://localhost:8000 - you should see 'Hello World'
  • connect to https://localhost:8000/redirect - you should receive an error page as the browser is redirected to http://localhost:8000/ssl (instead of https:// ...)

All 11 comments

Looks like the problem originates from werkzeug urls.py in one of the url_parse / url_unparse functions
https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/urls.py

Use url_for(..., _scheme="https") if you want url_for to return an absolute URL with https.

It could be sensible for flask.redirect to use the current scheme when getting protocol-relative URLs to detect whether it should return a https url, and just fallback to http when no req ctx is available.

Thank for pointing me to this - I tried and it works (also using _external=True)
I agree with your second comment, Flask.redirect might be smarter and to use the current scheme when available / detectable.
Thanks for your help

Actually flask.redirect is not responsible for this. Your code example should've worked. Check if the URL scheme is available inside of your request context, ie do print flask.request.environ['wsgi.url_scheme'] to check if your setup even passes the scheme to the flask app.

Also, Flask can't magically detect that it should redirect to anything else than the current port, so if you want to redirect from 5000 to 8000, you have to define explicitly so and can't just define the scheme.

What worked best for me in a similar situation is the ReverseProxied snippet.

Hi. My app works fine locally, but when I deploy it to Heroku, it keeps dropping from https to http. Can you help?

The problem I believe happens here:

return redirect(url_for('index'))

To add to this, I encountered a similar issue and solved it using the suggestion above to use the ReverseProxied snippet.

It looks like the Pallets' project removed their /snippets/ route from their site with the Flask 1.1.1 release, so for reference to the snippet suggested, there's a SO answer using it here:
https://stackoverflow.com/a/37842465/8188846

Thanks a lot !
I was walking through this issue for few days and redirect('xxxx', _external=True, _scheme="https") solved my problem.
I had the problem only on some smartphones using 4G (smartphone + wifi or laptop works fine)
One issue, it works on production but not on dev since it redirect to httpS://localhost:5000

I don't think this is a flask issue.

It is most likely a reverse proxy issue. In nginx, for instance location is rewritten by default using http.
try adding proxy_redirect http:// $scheme://; in your nginx reverse proxy configuration.

if you do that both

@app.route('/redirect/')
def redirectToSSL():
    return redirect(url_for('ssl'))

and

@app.route('/redirect/')
def redirectToSSL():
    return redirect('/ssl')

should work without modifications or special parameters, as it's the job of the reverse proxy to figure out how to rewrite the location header correctly. Only after hours of tests, however I have discovered that proxy_pass does not do that and you need proxy redirect as well for upstream location header rewrites ...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sungjinp11 picture sungjinp11  路  3Comments

stillesjo picture stillesjo  路  4Comments

westonplatter picture westonplatter  路  3Comments

rkomorn picture rkomorn  路  3Comments

rochacbruno picture rochacbruno  路  3Comments