8

I have a Flask application behind a Load balance that terminates SSL. I have code that "detects" when SSL is being used and mutates the request object:

@app.before_request
def before_request():
    x_forwarded_proto = request.headers.get('X-Forwarded-Proto')
    if  x_forwarded_proto == 'https':
        request.url = request.url.replace('http://', 'https://')
        request.url_root = request.url_root.replace('http://', 'https://')
        request.host_url = request.host_url.replace('http://', 'https://')

I then have a blueprint view function:

admin = Blueprint('admin', __name__, url_prefix='/admin')
@admin.route('/login')
def login():
    print request.url

The output of this function is (when I go to /admin/login) is always http:// instead of https:// (even though it should have been mutated in the before_request function.

Any ideas on how I can fix this?

codegeek
  • 32,236
  • 12
  • 63
  • 63
mmattax
  • 27,172
  • 41
  • 116
  • 149
  • Have you checked if the if x_forwarded_proto == 'https' evaluates to true ? – codegeek Nov 07 '13 at 16:35
  • @codegeek It does evaluate to true, see my solution. – mmattax Nov 07 '13 at 17:26
  • This is rather the wrong approach, just use the `ProxyFix` middleware, see [X-Forwarded-Proto and Flask](//stackoverflow.com/q/23347387), and the WSGI environment will be fixed up for you before Flask even begins creating a `request` object from it. – Martijn Pieters Jun 19 '17 at 18:18

1 Answers1

10

Turns out request is a proxied object. I'm not sure of the internals but it's "reset" on each import. I solved the issue by subclassing Request

class ProxiedRequest(Request):
    def __init__(self, environ, populate_request=True, shallow=False):
        super(Request, self).__init__(environ, populate_request, shallow)
        # Support SSL termination. Mutate the host_url within Flask to use https://
        # if the SSL was terminated.
        x_forwarded_proto = self.headers.get('X-Forwarded-Proto')
        if  x_forwarded_proto == 'https':
            self.url = self.url.replace('http://', 'https://')
            self.host_url = self.host_url.replace('http://', 'https://')
            self.base_url = self.base_url.replace('http://', 'https://')
            self.url_root = self.url_root.replace('http://', 'https://')

app = Flask(__name__);
app.request_class = ProxiedRequest
mmattax
  • 27,172
  • 41
  • 116
  • 149