Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@szul
Forked from Larivact/flask_script_name.md
Created May 28, 2019 17:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save szul/68c9245dff2eb7e19d6c3b713ce37a09 to your computer and use it in GitHub Desktop.
Save szul/68c9245dff2eb7e19d6c3b713ce37a09 to your computer and use it in GitHub Desktop.

Serving Flask under a subpath

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.

Conclusion

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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment