Flask request.remote_addr is wrong on webfaction and not showing real user IP
-
05-07-2021 - |
문제
I just deployed a Flask app on Webfaction and I've noticed that request.remote_addr
is always 127.0.0.1
. which is of course isn't of much use.
How can I get the real IP address of the user in Flask on Webfaction?
Thanks!
해결책
If there is a proxy in front of Flask, then something like this will get the real IP in Flask:
if request.headers.getlist("X-Forwarded-For"):
ip = request.headers.getlist("X-Forwarded-For")[0]
else:
ip = request.remote_addr
Update: Very good point mentioned by Eli in his comment. There could be some security issues if you just simply use this. Read Eli's post to get more details.
다른 팁
Rewriting the Ignas's answer:
headers_list = request.headers.getlist("X-Forwarded-For")
user_ip = headers_list[0] if headers_list else request.remote_addr
Remember to read Eli's post about spoofing considerations.
Werkzeug middleware
Flask's documentation is pretty specific about recommended reverse proxy server setup:
If you deploy your application using one of these [WSGI] servers behind an HTTP [reverse] proxy you will need to rewrite a few headers in order for the application to work [properly]. The two problematic values in the WSGI environment usually are
REMOTE_ADDR
andHTTP_HOST
... Werkzeug ships a fixer that will solve some common setups, but you might want to write your own WSGI middleware for specific setups.
And also about security consideration:
Please keep in mind that it is a security issue to use such a middleware in a non-proxy setup because it will blindly trust the incoming headers which might be forged by malicious clients.
The suggested code (that installs the middleware) that will make request.remote_addr
return client IP address is:
from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, num_proxies=1)
Note num_proxies
which is 1
by default. It's the number of proxy servers in front of the app.
The actual code is as follows (lastest werkzeug==0.14.1
at the time of writing):
def get_remote_addr(self, forwarded_for):
if len(forwarded_for) >= self.num_proxies:
return forwarded_for[-self.num_proxies]
Webfaction
Webfaction's documentation about Accessing REMOTE_ADDR
says:
...the IP address is available as the first IP address in the comma separated list in the
HTTP_X_FORWARDED_FOR
header.
They don't say what they do when a client request already contains X-Forwarded-For
header, but following common sense I would assume they replace it. Thus for Webfaction num_proxies
should be set to 0
.
Nginx
Nginx is more explicit about it's $proxy_add_x_forwarded_for
:
the “X-Forwarded-For” client request header field with the
$remote_addr
variable appended to it, separated by a comma. If the “X-Forwarded-For” field is not present in the client request header, the$proxy_add_x_forwarded_for
variable is equal to the$remote_addr
variable.
For Nginx in front of the app num_proxies
should be left at default 1
.
You can use request.access_route
to access list of ip :
if len(request.access_route) > 1:
return request.access_route[-1]
else:
return request.access_route[0]
Update:
You can just write this:
return request.access_route[-1]
The problem is there's probably some kind of proxy in front of Flask. In this case the "real" IP address can often be found in request.headers['X-Forwarded-For']
.