Your Flask app object implements the __call__
method, which means it can be called like a regular function.
When your WSGI container receives a HTTP request it calls your app with the environ
dict and the start_response
callable.
WSGI is specified in PEP 0333.
The two relevant environ variables are:
SCRIPT_NAME
The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server.
PATH_INFO
The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash.
Flask's routing and url_for
are provided by Werkzeug. Werkzeug's routing operates on PATH_INFO
and its url_for
function prepends SCRIPT_NAME
.
This means that if your application isn't located at the root of your server but under a path you have to tell your WSGI container the env SCRIPT_NAME
. It will then split incoming request paths into SCRIPT_NAME
and PATH_INFO
.
In production
How you do this depends on your deployment option. Eg. with Gunicorn you can pass environment variables with -e
.
gunicorn -e SCRIPT_NAME=/my-app my_app:app
With Flask's dev server
Flask's builtin development server app.run()
uses werkzeug.serving.run_simple()
. In order to use the convenient debugging and reloading features while still serving the site under a SCRIPT_NAME
you would have to use a WSGI middleware that sets the SCRIPT_NAME
and trims the PATH_INFO
.
from werkzeug.serving import run_simple
from my_app import app
class FixScriptName(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
SCRIPT_NAME = '/my-app'
if environ['PATH_INFO'].startswith(SCRIPT_NAME):
environ['PATH_INFO'] = environ['PATH_INFO'][len(SCRIPT_NAME):]
environ['SCRIPT_NAME'] = SCRIPT_NAME
return self.app(environ, start_response)
else:
start_response('404', [('Content-Type', 'text/plain')])
return ["This doesn't get served by your FixScriptName middleware.".encode()]
app = FixScriptName(app)
run_simple('0.0.0.0', 5000, app, use_reloader=True)
Thanks, it was not obvious to me that you have to run this using
python my_app.py
instead of the usualflask run
.