Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling flask url_for behind nginx reverse proxy

I have a flask application using nginx for a reverse proxy/ssl termination, but I'm running into trouble when using url_for and redirect in flask.

nginx.conf entry:

location /flaskapp {
  proxy_pass http://myapp:8080/;
  proxy_set_header Host $http_host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

The idea is that a user navigates to

https://localhost:port/flaskapp/some/location/here

and that should be passed to flask as

http://localhost:8080/some/location/here

This works reasonably well when navigating to a defined route, however if the route has redirect(url_for('another_page')), the browser is directed to

http://localhost:8080/another_page

And fails, when the URL I actually want to go to is:

https://localhost:port/flaskapp/another_page

I have tried several other answers for similar situations, but none have seemed to be doing exactly what I am doing here. I have tried using _external=True, setting app.config['APPLICATION_ROOT'] = '/flaskapp' and many iterations of different proxy_set_header commands in nginx.conf with no luck.

As an added complication, my flask application is using flask-login and CSRF cookies. When I tried setting APPLICATION_ROOT the application stopped considering the CSRF cookie set by flask-login valid, which I assume has something to do with origins.

So my question is, how do I make it so that when flask is returning a redirect() to the client, nginx understands that the URL it is given needs flaskapp written into it?

like image 553
mstorkson Avatar asked Oct 30 '19 20:10

mstorkson


People also ask

How to use nginx as reverse proxy for flask app?

Here are the steps to use NGINX as reverse proxy for Flask app. Since Flask is only a framework, you will need a python application interface such as uwsgi or gunicorn to run the Flask application. Flask will run at a port other than http port 80 since NGINX will be running on it.

What is the use of proxy_set_header in flask?

The proxy_set_header will set the __Host header__ for the request between our nginx and flask, this helps to avoid certain errors, specially if one is using Django instead of flask, the ALLOWED_HOSTS setting would require this Host header. we mount this file in our nginx container with the following line

How to build a Nginx reverse proxy container?

Complete the following steps to build the Nginx reverse proxy container on your local system. 6.1 —Build the container using Docker. Complete the following command from the project directory: This command builds a container using the Dockerfile in the current directory and tags the container nginx-container.

What is Nginx and how does it work?

Nginx is an open-source HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server. Nginx is known for its high performance, stability, rich feature set, simple configuration, and low resource consumption.


Video Answer


2 Answers

I managed to fix it with some changes.

Change 1. Adding /flaskapp to the routes in my flask application. This eliminated the need for URL-rewriting and simplified things greatly.

Change 2. nginx.conf changes. I added logc in the location block to redirect http requests as https, new conf:

location /flaskapp {
  proxy_pass http://myapp:8080/;
  proxy_set_header Host $http_host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  # New configs below
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-Proto $scheme;
  # Makes flask redirects use https, not http.
  proxy_redirect http://$http_host/ https://$http_host/;
}

While I didn't "solve" the issue of introducing conditional rewrites based on a known prefix, since I only need one prefix for this app it is an acceptable solution to bake it into the routes.

like image 65
mstorkson Avatar answered Oct 19 '22 18:10

mstorkson


In your situation I think the correct thing would be to use werkzeug's ProxyFix middleware, and have your nginx proxy set the appropriate required headers (specifically X-Forwarded-Prefix).

https://werkzeug.palletsprojects.com/en/0.15.x/middleware/proxy_fix/#module-werkzeug.middleware.proxy_fix

This should make url_for work as you would expect.

Edit: Snippet from @Michael P's answer

from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
like image 26
niltz Avatar answered Oct 19 '22 19:10

niltz