Flask behind a reverse proxy: actual client IPs
Hi! At work I'm involved with a REST API based on
Flask. For SSL, we decided to use nginx as a
reverse proxy. As a result, client IPs are all reported to be 127.0.0.1
:
* Running on http://0.0.0.0:5000/
127.0.0.1 - - [15/Feb/2015 17:43:48] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [15/Feb/2015 17:43:48] "GET /favicon.ico HTTP/1.1" 404 -
Flask is based on Werkzeug. Werkzeug comes with a helper called ProxyFix
to
address this problem.
from flask import Flask from werkzeug.contrib.fixers import ProxyFix app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app) [..]
To make nginx feed the headers needed by ProxyFix
, these lines help:
proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Host your.project.domain.org; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:5000/;
Now one thing remains to fix: The debugging log on stderr still reports
127.0.0.1
. To get the IP from header X-Forwarded-For
in there, I made this
patching function replacing method WSGIRequestHandler.address_string
:
def fix_werkzeug_logging(): from werkzeug.serving import WSGIRequestHandler def address_string(self): forwarded_for = self.headers.get( 'X-Forwarded-For', '').split(',') if forwarded_for and forwarded_for[0]: return forwarded_for[0] else: return self.client_address[0] WSGIRequestHandler.address_string = address_string
With that applied, I get actual client IPs. Tested with python-flask 0.8-1 and python-werkzeug 0.8.3+dfsg-1 of Debian wheezy. All source code in this post is licensed under CC0.