1

I am trying to run a Flask app on Gunicorn through an Nginx server. I would like the app to run on a sub-directory instead of through a different port, if possible, but all I get are 404 errors. Here is my conf file, which is an included file in the conf.d folder:

server {
    listen          80;
    server_name     127.0.0.1;
    location / {
        root /var/www/html;
    }
    location /chess/ {
        proxy_pass http://unix:/usr/share/nginx/sockets/chess.sock;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Could someone please tell me how to do this? I have looked all over and tried a lot of different things, to no avail. It runs fine on a different port, but that is not what I want. A subdomain is also a suitable option, but I can only get that to work in production, not development, for some reason. Someone posed the question here but the link they gave to the solution is dead.

spiff
  • 308
  • 1
  • 3
  • 15

2 Answers2

2

when do you get 404's? when you access /chess/? please attach the flask app's code.

anyway, the following certainly works, I tested it.

followed this guide

project directory

myproject.py:

from flask import Flask
app = Flask(__name__)

@app.route("/chess/",defaults={'name': None})
@app.route("/chess/<name>")
def hello(name):
    if name is None:
        name="!"
    else: 
        name = ", " + name
    # PLEASE don't use the following line of code in a real app, it creates a self-xss vulnerability. ALWAYS sanitize user input. 
    return f"<h1 style='color:blue'>Hello There{name}</h1>"

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

nginx - /etc/nginx/sites-enabled/myproject (symlink)

server {
    listen 8080;
    server_name your_domain www.your_domain;


    location / {
        root /home/username/myproject/static/;
    }



    location /chess/ {
        include proxy_params;
        proxy_pass http://unix:/home/username/myproject/myproject.sock;
    }
}

<host>:8080/chess/stackoverflow:

flask-app

<host>:8080/a.html: (actually served from /home/myproject/static)

static-file

generally and for future reference - try looking at nginx logs (/var/log/nginx) or service logs (journalctl -u myproject or systemctl status myproject)

Yarin_007
  • 1,449
  • 1
  • 10
  • 17
  • I am up-voting and awarding the bounty to your answer because it got me on the right track, but I can't mark it as the correct one because the point of the question was to stay on port 80. I found my solution elsewhere. See my answer. – spiff Oct 26 '22 at 03:58
  • Got it, thank you! glad to hear it worked out. just for the record port 80 would have worked just as well. the "best practice" doesn't make much sense, as the gunicorn (in your case) isn't "listening" on any network port at all, you're communicating with it via unix-socket. all the added proxy-pass is doing is moving it from nginx to itself (from port 80 to 8080), (and then forwarding it from port 8080 to gunicorn via unix-socket) – Yarin_007 Oct 26 '22 at 20:46
  • I played around some more and found out you are absolutely right, so I modified my answer to incorporate yours. However, the dual proxy_pass method has it's advantage for situations like using a subdomain, so I kept it. – spiff Oct 27 '22 at 21:55
1

Two methods are available. The first method involves adding the subpath to both the NGINX configuration and the Flask app, as suggested in the answer by Yarin_007. Set up NGINX like so:

server {
    listen localhost:80;
    location / {
        root /var/www/html;
    }
    location /chess/ {
        proxy_pass http://unix:/usr/share/nginx/sockets/chess.sock;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
   }
}

And alter the Flask app to include the subpath:

from flask import Flask, request
...
@app.route("/chess/", methods=["GET", "POST"])
...

The alternative, as suggested in this answer is to run the Gunicorn service on a non-privileged port (or maybe a subdomain) and use two proxy_pass directives to direct from the port 80 subpath like so:

server {
    listen  localhost:80;
    location / {
        root /var/www/html;
    }
    location /chess/ {
        proxy_pass http://localhost:8080/;
    }
}

server {
    listen  localhost:8080;
    location / {
        proxy_pass http://unix:/usr/share/nginx/sockets/chess.sock;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

The Flask app can remain as it was originally:

from flask import Flask, request
...
@app.route("/", methods=["GET", "POST"])
...
spiff
  • 308
  • 1
  • 3
  • 15