Make Flask's url_for use the 'https' scheme in an AWS load balancer without messing with SSLify Make Flask's url_for use the 'https' scheme in an AWS load balancer without messing with SSLify python python

Make Flask's url_for use the 'https' scheme in an AWS load balancer without messing with SSLify


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.ProxyFixcall in my code.example:

from werkzeug.contrib.fixers import ProxyFixapp = 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)


Digging around Flask source code, I found out that url_for uses the Flask._request_ctx_stack.top.url_adapter when there is a request context.

The url_adapter.scheme defines the scheme used. To make the _scheme parameter work, url_for will swap the url_adapter.scheme temporarily and then set it back before the function returns.

(this behavior has been discussed on github as to whether it should be the previous value or PREFERRED_URL_SCHEME)

Basically, what I did was set url_adapter.scheme to https with a before_request handler. This way doesn't mess with the request itself, only with the thing generating the urls.

def _force_https():    # my local dev is set on debug, but on AWS it's not (obviously)    # I don't need HTTPS on local, change this to whatever condition you want.    if not app.debug:         from flask import _request_ctx_stack        if _request_ctx_stack is not None:            reqctx = _request_ctx_stack.top            reqctx.url_adapter.url_scheme = 'https'app.before_request(_force_https)