I've recently added a SSL certificate to my webapp. It's deployed on Amazon Web Services uses load balancers. The load balancers work as reverse proxies, handling external HTTPS and sending internal HTTP. So all traffic to my Flask app is HTTP, not HTTPS, despite being a secure connection.
Because the site was already online before the HTTPS migration, I used SSLify to send 301 PERMANENT REDIRECTS
to HTTP connections. It works despite all connections being HTTP because the reverse proxy sets the X-Forwarded-Proto
request header with the original protocol.
url_for
doesn't care about X-Forwarded-Proto
. It will use the my_flask_app.config['PREFERRED_URL_SCHEME']
when a scheme isn't available, but during a request a scheme is available. The HTTP scheme of the connection with the reverse proxy.
So when someone connects to https://example.com
, it connects to the load balancer, which then connects to Flask using http://example.com
. Flask sees the http
and assumes the scheme is HTTP, not HTTPS as it originally was.
That isn't a problem in most url_for
used in templates, but any url_for
with _external=True
will use http instead of https. Personally, I use _external=True
for rel=canonical
since I heard it was recommended practice. Besides that, using Flask.redirect
will prepend non-_external urls with http://example.com
, since the redirect header must be a fully qualified URL.
If you redirect on a form post for example, this is what would happen.
https://example.com/form
303 SEE OTHER
to http://example.com/form-posted
301 PERMANENT REDIRECT
to https://example.com/form-posted
Every redirect becomes 2 redirects because of SSLify.
https://stackoverflow.com/a/26636880/1660459
my_flask_app.config['PREFERRED_URL_SCHEME'] = 'https'
Doesn't work because there is a scheme during a request, and that one is used instead. See https://github.com/mitsuhiko/flask/issues/1129#issuecomment-51759359
https://stackoverflow.com/a/28247577/1660459
def _force_https(app): def wrapper(environ, start_response): environ['wsgi.url_scheme'] = 'https' return app(environ, start_response) return wrapper app = Flask(...) app = _force_https(app)
As is, this didn't work because I needed that app later. So I used wsgi_app instead.
def _force_https(wsgi_app): def wrapper(environ, start_response): environ['wsgi.url_scheme'] = 'https' return wsgi_app(environ, start_response) return wrapper app = Flask(...) app.wsgi_app = _force_https(app.wsgi_app)
Because wsgi_app
is called before any app.before_request
handlers, doing this makes SSLify think the app is already behind a secure request and then it won't do any HTTP-to-HTTPS redirects.
(I can't even find where I got this one from)
from functools import partial import Flask Flask.url_for = partial(Flask.url_for, _scheme='https')
This could work, but Flask will give an error if you set _scheme
but not _external
. Since most of my app url_for
are internal, it doesn't work at all.
Flask url_for is defined as a function that enables developers to build and generate URLs on a Flask application. As a best practice, it is the url_for function that is required to be used, as hard coding the URL in templates and view function of the Flask application tends to utilize more time during modification.
The url_for() function generates the URL to a view based on a name and arguments. The name associated with a view is also called the endpoint, and by default it's the same as the name of the view function.
I was having these same issues with `redirect(url_for('URL'))' behind an AWS Elastic Load Balancer recently & I solved it this using the werkzeug.contrib.fixers.ProxyFix call in my code. example:
from werkzeug.contrib.fixers import ProxyFix app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app)
The ProxyFix(app.wsgi_app)
adds HTTP proxy support to an application that was not designed with HTTP proxies in mind. It sets REMOTE_ADDR, HTTP_HOST from X-Forwarded headers.
Example:
from werkzeug.middleware.proxy_fix import ProxyFix # App is behind one proxy that sets the -For and -Host headers. app = ProxyFix(app, x_for=1, x_host=1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With