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.

The Application

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:

  • flaskapp
    • app
      • __init__.py
      • views.py
      • static
        • python.png
      • templates
        • index.html
    • run.py

flaskapp/app/__init__.py

from flask import Flask

app = Flask(__name__)
from . import views

flaskapp/app/views.py

from flask import render_template
from . import app

@app.route('/')
def index():
    return render_template('index.html')

flaskapp/app/templates/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

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 :)

Reverse proxy

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

Links