20

What I am trying to achieve:

I have an nginx web server hosting mydomain.com. When someone types my domain.com into their client I would like my server to serve index.html from /var/www/mydomain/ When they type mydomain.com/flaskapp1 they should see flaskapp1. When they type mydomain.com/flaskapp2 they should see flaskapp2.

I have managed to get one or the other flask apps served using the tutorial here http://www.markjberger.com/flask-with-virtualenv-uwsgi-nginx/ but when trying to implement serving two separate flask apps I run into difficulty. Instead of seeing the flask app I get a 404 message when I try to access either of the flask apps with mydomain.co.uk/flaskapp or mydomain.co.uk/flaskapp2 in a browser.

This is what I have so far:

cat /etc/nginx/sites-available/mydomain.co.uk

server {
       listen 80;

        server_name www.mydomain.co.uk mydomain.co.uk;

        location / {
               root /var/www/html/;
               index index.html index.htm;
        }

        location /flaskapp {
                include uwsgi_params;
                uwsgi_pass unix:/tmp/flaskapp.sock;
        }

         location /flaskapp2 {
              include uwsgi_params;
              uwsgi_pass unix:/tmp/flaskapp2.sock;
         }
 }

The above conf file has been sim linked into /etc/nginx/sites-enabled.

cat /etc/uwsgi/apps-available/flaskapp.ini

[uwsgi]
vhost = true
socket = /tmp/flaskapp.sock
venv = /var/www/flaskapp/venv
chdir = /var/www/flaskapp
module = flaskapp
callable = app

cat /etc/uwsgi/apps-available/flaskapp2.ini

[uwsgi]
vhost = true
socket = /tmp/flaskapp2.sock
venv = /var/www/flaskapp2/venv
chdir = /var/www/flaskapp2
module = flaskapp2
callable = app

Both .ini files have been symlinked into /etc/uwsgi/apps-enabled. UWSGI restarts fine without any issues and is up and running. Both flaskapp.sock and flaskapp2.sock are owned by www-data

cat /var/www/flaskapp/flaskapp.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World from flaskapp1!'

if __name__ == '__main__':
    app.run(host='0.0.0.0')

cat /var/www/flaskapp2/flaskapp2.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World from flaskapp2!'

if __name__ == '__main__':
    app.run(host='0.0.0.0')

cat /var/www/mydomain.co.uk/index.html

<!DOCTYPE html>
<html>
<body>

<h1>mydomain.co.uk</h1>

<p>This is the index page of my domain.co.uk</p>

</body>
</html>

Both virtual environments have flask installed and will run the flask apps using the development server.

I hope it's something simple that I've missed.

Hoppo
  • 1,130
  • 1
  • 13
  • 32

3 Answers3

13

Looking at the uwsgi documentation for NGINX here.

Specifically:

Unfortunately nginx is not able to rewrite PATH_INFO accordingly to SCRIPT_NAME. For such reason you need to instruct uWSGI to map specific apps in the so called “mountpoint” and rewrite SCRIPT_NAME and PATH_INFO automatically:

Changing my flaskapp.ini and flaskapp2.ini files to contain the mount points for the apps and turning on the manage-script-name variable has worked.

cat /etc/uwsgi/apps-available/flaskapp.ini

[uwsgi]
vhost = true
socket = /tmp/flaskapp.sock
venv = /var/www/flaskapp/venv
chdir = /var/www/flaskapp
module = flaskapp
callable = app
mount = /flaskapp=flaskapp.py
manage-script-name = true

cat /etc/uwsgi/apps-available/flaskapp2.ini

[uwsgi]
vhost = true
socket = /tmp/flaskapp2.sock
venv = /var/www/flaskapp2/venv
chdir = /var/www/flaskapp2
module = flaskapp2
callable = app
mount = /flaskapp2=flaskapp2.py
manage-script-name = true

And now both flask apps are running via uwsgi through nginx as required.

vvvvv
  • 25,404
  • 19
  • 49
  • 81
Hoppo
  • 1,130
  • 1
  • 13
  • 32
  • 2
    How would we configure supervisor in such a way where it works with nginx on a reverse proxy on a subdomain? – user805981 Dec 01 '17 at 14:02
6

The problem is here: both flask apps have no idea that they were served from subdirectory in your domain. Nginx is passing full URL to them, not URL relative to that directory, so every URL is dispatched relative to root. Let's see that in example:

Assume that you have 3 views in your flaskapp, served on urls /one/, /two/ and /three/. So when you're trying to access view on URL /one/, you are typing address: http://yourdomain.com/flaskapp/one/. Flask will receive from nginx URL /flaskapp/one/ but there is no such view in that app, so it will send 404 in response.

What you can do is inform each flaskapp that they aren't served on domain root, but on particular subdirectory. You can achieve this by sending SCRIPT_NAME uwsgi_param with subdirectory location in value, so your nginx config will have:

location /flaskapp {
    include uwsgi_params;
    uwsgi_pass unix:/tmp/flaskapp.sock;
    uwsgi_param SCRIPT_NAME /flaskapp;
}

location /flaskapp2 {
    include uwsgi_params;
    uwsgi_pass unix:/tmp/flaskapp2.sock;
    uwsgi_param SCRIPT_NAME /flaskapp2;
}
vvvvv
  • 25,404
  • 19
  • 49
  • 81
GwynBleidD
  • 20,081
  • 5
  • 46
  • 77
  • 1
    This didn't seem to make any difference, however it did point me in the direction of the correct answer so have marked it up as useful. – Hoppo Jan 11 '16 at 11:59
  • Maybe flask application need some tweak to understand `SCRIPT_NAME` correctly. I've never used `SCRIPT_NAME` with flask, but I know that is solution with django (which can use same nginx an uWSGI configuration). – GwynBleidD Jan 11 '16 at 12:04
2

Adding the mounting point worked for me.

/etc/uwsgi/apps-available/flaskapp.ini

callable = app
mount = /flaskapp=/var/www/flaskapp

/etc/uwsgi/apps-available/flaskapp2.ini

callable = app
mount = /flaskapp2=/var/www/flaskapp2

Source https://neginfinity.bitbucket.io/2017/09/hosting-multiple-flask-applications-in-nginx.html

DPalharini
  • 413
  • 6
  • 16