Lately I wanted to test a new application before deployment and wanted to make it accessible via a subdirectory of my blog url. It doesn't sound like a great deal but I had some difficulties to accomplish it. To make my life easier in future and to help others facing the same challenge I wrote this Howto.
For demonstration purposes we'll create a small Flask app. An important issue when you put a WSGI application in a subdirectory is about pathes and filenames. So we'll create an app that uses a template and a static file. The application structure looks like this:
flaskappapp__init__.pyviews.pystaticpython.pngtemplatesindex.htmlrun.pyfrom flask import Flask
app = Flask(__name__)
from . import views
from flask import render_template
from . import app
@app.route('/')
def index():
return render_template('index.html')
<h1>Welcome to App1</h1>
Image "{{ url_for('static', filename='python.png') }}":<br/>
<img src="{{ url_for('static', filename='python.png') }}">
Originally the app is served from http://servername.com so all Flask routes and also
static file pathes will be interpreted relatively to the root. Flask will take care of
this automatically if you use the url_for function to create links in your templates
and the route decorator in your views module. But If you serve from a subdirectory you
have to tell Flask that your root is a different one. So our {{ url_for() }} function
in index.html should deliver http://servername.com/app1/static/python.png instead of
the original http://servername.com/static/python.png. We accomblish this by using a
middleware. For running our application we will create the file flaskapp/run.py
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from app import app
application = DispatcherMiddleware(
None, {
'/app1': app
}
)
if __name__ == '__main__':
run_simple('localhost', 5000, application, use_reloader=True)
The
werkzeug.wsgi.DispatcherMiddleware
class allows one to mount middlewares or applications in a WSGI application. This is
useful if you want to combine multiple WSGI applications. And that is what we want to do
here. We don't have a main application for the server root directory so we will provide
None as first argument. The second argument is a dictionary containing the url
subdirectories as keys and the corresponding WSGI objects as values. Now we can run our
Flask application by calling:
$ python run.py
We visit http://localhost:5000/app1/ and find a our small Python logo facing us. Success :)
I use nginx as a reverse proxy. So for my new app to work I created a new configuration file /etc/nginx/sites-available/app1.
upstream app1 {
server 127.0.0.1:5000;
}
server {
listen *:80;
root /home/app1/flaskapp1/app;
location /app1/static {
alias /home/app1/flaskapp1/app/static;
}
location /app1 {
proxy_pass http://app1;
}
}
It basically passes all calls to /app1 on to the WSGI application and serves static
files separately from the static directory. Put a symbolical link to this file in
/etc/nginx/sites-enabled to enable it and restart nginx.
service nginx restart