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