David

Using SCRIPT_NAME in Flask standalone

This is a variant of the ReverseProxied solution from pocoo. As it depends on __call__ being called inside a wsgi environment, it does not work when the app is being run standalone with run().

ScriptNameHandler

We solve this by instead overriding the make_environ() function of WSGIRequestHandler inside a new class, ScriptNameHandler, and passing it as an argument to Werkzeug’s run_simple(), like so:

from werkzeug.serving import WSGIRequestHandler

class ScriptNameHandler(WSGIRequestHandler):
    def make_environ(self):
        environ = super().make_environ()
        script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
        if script_name:
            environ['SCRIPT_NAME'] = script_name
            path_info = environ['PATH_INFO']
        if path_info.startswith(script_name):
            environ['PATH_INFO'] = path_info[len(script_name):]

        scheme = environ.get('HTTP_X_SCHEME', '')
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        return environ

Then change

app.run()

to

app.run(request_handler=ScriptNameHandler)

Passing the http header X-SCRIPT-NAME to the app creates the value HTTP_X_SCRIPT_NAME in the environ variable.

Apache example

<Location "/test_subdir">
    ProxyPass "http://localhost:8081" 
    X-SCRIPT-NAME "/test_subdir"
</Location>