request.headers.get('X-Forwarded-Proto', 'http') == 'https'
Really should be checking against the wsgi.url_scheme. Just trusting HTTP headers from the client is generally not the best idea. If yuo want to use X-Forwarded-Proto you can still remap them as necessary in a WSGI middleware.
I need to use X-Forwarded-Proto for reverse proxied SSL termination. Seems like there's little benefit to moving to middleware?
The only clean way to handle this is to check the wsgi environ, specifically wsgi.url_scheme (which is exactly what's done by request.is_secure). If requests are actually secure, but wsgi.url_scheme == 'http', something is configured wrong.
wsgi.url_scheme == 'http'
Other places inside Flask/Werkzeug also rely solely on wsgi.url_scheme, like for example redirect('/some/relative/url'), which will send an https://-based Location header iff wsgi.url_scheme == 'https'.
wsgi.url_scheme == 'https'
Using Flask-SSLify on Heroku for me led to the following odd case:
I hadn't configured gunicorn correctly to set the wsgi.url_scheme based on the X-Forwarded-Proto header (as received from the Heroku reverse proxy). Therefore, all my requests weren't secure, according to request.is_secure. But since Flask-SSLify had this explicit header check in place, it would auto-redirect me to https:// URLs nicely. The effect: on each redirect('/relative/url'), Werkzeug was generating http-based redirect URLs. So I was flip-flopping between HTTP and HTTPS continuously.
If Flask-SSLify didn't do this check, it would've been clear immediately that my wsgi environ setup was wrong, so I'm with @mitsuhiko on this one.
Removes client header check per kennethreitz/flask-sslify#3
I had the same problem as @nvie on Heroku. When a trailing slash is missing, the redirect to the same path with slash present happens as expected; however, the protocol changes from https to http. I used the ProxyFix fixer from Werkzeug to remedy.
from flask import Flask
from werkzeug.contrib.fixers import ProxyFix
from flask_sslify import SSLify
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
SSLify(app, age=300, permanent=True)
return 'Hello World!'
With that setup, a request to https://domain.tld/hello correctly redirects to https://domain.tld/hello/ instead of http://domain.tld/hello.
Just to reiterate what others have said here, explicitly checking the X-Forwarded-Proto here should not be needed and may cause confusion when parts of the application appear to be secure and others do not.
If this change would be accepted, I'm happy to make it and add some documentation regarding proxies to accompany it.
So If I were to use the code sample given by kljensen that includes ProxyFix, would that work properly on Heroku?
This should force SSL properly