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?
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.
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
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.
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.
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With