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.

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 and HTTP_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.

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.

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'].