Fixing Django Comment IPs at Webfaction

If you’ve ever run a Django application at Webfaction which uses the builtin contrib.comments app, you’ll soon realize that all the comment IP addresses are the same: 127.0.0.1. This will give you the impression that all the commenters are commenting from the same webserver as your site is hosted — don’t worry, that’s not the case.

It happens because Webfaction uses a reverse proxy to receive all requests, and forward them to the server instance which handles your Django application. Since the comment is (techincally) coming from the reverse proxy, the IP address is recorded as 127.0.0.1.

However, this can be fixed, and in a fairly simple way. The reverse proxy sets the X-Forwarded-For HTTP header in the request, so that you can tell where the request originally came from. It’s just a matter of using the IP address as specified in this header.

A note of warning is necessary here. In most cases, you cannot trust the contents of the X-Forwarded-For header, and it can easily be used to spoof an IP address, making you think that it’s someone it’s not. As such, use the following method only when you can trust where your proxied requests are coming from, in this particular situation, you can.

The fix is fairly simple, just tell Django to replace the request’s IP address with the one in the header. Add “django.middleware.http.SetRemoteAddrFromForwardedFor” to your list of middleware in settings.py.

In Django 1.1 (which incidentally, released on Wednesday), the above middleware was removed because it may lead to the faulty assumption that the IP address contained in the X-Forwarded-For header is in any way “safe” as a reliable source of authentication. If you’re sure that your proxying server is trustworthy (which again, in this case it is), you simply have to duplicate the middleware code yourself.

The code for the middleware itself is quite small, at least without the docstring :)

class SetRemoteAddrFromForwardedFor(object) :
    def process_request(self, request) :
        try :
            real_ip = request.META['HTTP_X_FORWARDED_FOR']
        except KeyError :
            return None
        else :
            real_ip = real_ip.split(",")[0].strip()
            request.META['REMOTE_ADDR'] = real_ip

Just put that somewhere where it’s importable, and add it to your middleware list, and you should be done.

Respond

Comments for this entry have been closed.