Flask on Shared Hosting

In the course of experimenting with Python and Flask, I decided I wanted to deploy a Flask-based Rest API to the hosting provider that I use. I admit, this is not nearly so cool and modern as using a Docker container or hosting it as a Serverless Lambda app in AWS. Since I am already paying for this web site, however, deploying it into my existing shared hosting account means it costs me nothing extra. An easy decision from a financial perspective. I use a2hosting, so what follows is based on my experience with their service (which by the way is awesome).

Project Structure

Use the ‘Setup Python App’ interface in CPanel. This will give you a few simple options to start with. The Python versions available, the path to the application under your home directory, and the domain/URI where your app will reside.

With my hosting provider, this added a folder with the URI path name under the root folder of the domain or subdomain selected. So, for example, when I configured the application to exist at api.benjaminpack.com/flaskapp, I ended up with a new folder named ‘flaskapp’ under the ‘api_subdomain’ folder that hosts that subdomain. Had I picked my primary domain, this would have gone into ‘public_html’ instead

Even though you defined a project directory in the setup interface, you don’t actually have to use it (though you should leave it in place with any files it creates, otherwise the Setup Python App UI might display errors). I placed the pyton code for my application directly in the folder created under the domain folder. The virtualenv that gets created, however, does have to be used. To activate that and add the necessary dependencies to get things started, you’ll need to run the following (replacing the path with what you see in the Python selector in CPanel).

source /home/user/virtualenv/venv_name/3.5/bin/activate
pip install flask flup6

Note, flup6 is a Python package that provides an fcgi server to Python 3 applications. If you selected a python 2.x version then you’ll need to install the original flup package. This will also require a change to the .fcgi file. We haven’t talked about that yet so here it is.

The FCGI File

At the time I was putting this together, my hosting provider did not fully support WSGI so I had to fall back to using the FastCGI protocol. This required the addition of a simple FCGI file that runs my Flask application using the flup WSGI server that implements the FastCGI protocol. My flask application uses an application factory so this application.fcgi file ends up being really simple.

#!/home/user/virtualenv/venv_name/3.5/bin/python
from flup.server.fcgi import WSGIServer
from flaskapp import create_app

if __name__ == '__main__':
    app = create_app()
    WSGIServer(app).run()

Note that the first line references the python install in your virtual environment. You’ll need to update this line to reflect the path to the python executable in your virtualenv. Also worth noting, the FCGI file needs to be executable for this to work.

chmod +x application.fcgi

The .htaccess File

Now we need to tell Apache what to do if someone accesses the context path we configured. In this case, it needs to route to the fcgi file. To accomplish this, we edit the .htaccess file in our domain’s application directory. This file was likely created by the Python setup earlier. If not, you’ll need to create it. Either way, the file needs to be edited to include the following.

Options +ExecCGI
AddHandler fcgid-script .fcgi
RewriteEngine on
# The following lines are for FastCGI:
RewriteCond %{REQUEST_FILENAME} !=/home/user/api_subdomain/flaskapp/application.fcgi
RewriteRule ^(.*)$ application.fcgi/$1 [QSA,L]

Note that your .htaccess file might include might include some entries about Passenger configuration. This was a source of some confusion for me when I was first trying to get a Flask app working. These settings are used by the Setup Python App CPanel interface to display your application in the UI after it is set up. If you remove these lines your app might continue to work but it will no longer show in the python selector UI. Mine looked like the following:

# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION BEGIN
PassengerAppRoot "/home/user/projects/flaskapp"
PassengerBaseURI "/flaskapp"
PassengerPython "/home/user/virtualenv/venv_name/3.5/bin/python3.5"
# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION END

That’s It

If everything is set up correctly you should now be able to access your Flask app at the /flaskapp context path on your domain. The code I used as a prototype for this post is available on this github page. I should point out that this example is simple if not simplistic. Full fledged Flask apps might need additional set-up in the FCGI file or extra configuration in the .htaccess file for dealing with things like static content. Your hosting provider may do things differently from mine. Even with those caveats, I hope this has helped. For additional info about deploying your application with FCGI, you can check out the Flask documentation - http://flask.pocoo.org/docs/0.12/deploying/fastcgi/.