When running Django behind Nginx as a reverse proxy, the
request.META['REMOTE_ADDR'] may store the proxy IP address (eg. 127.0.0.1)
rather than the client's IP address. To get the client's IP address in Django,
you can set the X-Forwarded-For HTTP header in your nginx proxy configuration:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
And then access it in Django with request.META['HTTP_X_FORWARDED_FOR']:
ip = request.META['HTTP_X_FORWARDED_FOR']
You would use request.META['HTTP_X_FORWARDED_FOR'] only when running Django
behind a reverse proxy where you know that your server is setting
the value and it is not being sent in the request. Therefore, getting the IP
from request.META['HTTP_X_FORWARDED_FOR'] vs request.META['REMOTE_ADDR'] must
be determined at the project-level and not within a reusable app.
So, if you don't want your reusable apps using conditionals to determine where
to get the IP address, what do you do? Well, I use a variation of the infamous
SetRemoteAddrFromForwardedFor middleware. Once upon a time, Django shipped with
this middleware to set the value of request.META['REMOTE_ADDR'] to
request.META['HTTP_X_FORWARDED_FOR'] if it exists.
It was removed in Django 1.2
as it was deemed too easy to be used incorrectly.
I create XForwardedForMiddleware and put it into middleware.py at the
project-level.
class XForwardedForMiddleware():
def process_request(self, request):
if 'HTTP_X_FORWARDED_FOR' in request.META:
request.META['REMOTE_ADDR'] = request.META['HTTP_X_FORWARDED_FOR']
return None
With this middleware installed, reusable apps can simply use
request.META['REMOTE_ADDR'] and be blissfully unaware of the fact that it's
behind a proxy.
