Reading CloudFlare headers in a Django middleware
For my new Django project, DB Buddy, I’m using CloudFlare as my CDN. It has a bunch of useful features that would otherwise take extra work, such as DDoS protection, HTML minification, and analytics.
Since CDN’s proxy your site, all requests are seen to be coming from their servers’ IP’s, rather than actual users’ IP’s. But sometimes you need to inspect real user IP’s, for example to implement rate limiting. To help with this, CloudFlare adds several useful headers, including
CF-Connecting-IP with the client’s real IP.
In order to provide an interface to get the client’s IP, whether my project is running in development or production, I added a middleware to attach the client IP as
class CloudflareMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): try: ip = request.headers["CF-Connecting-IP"] except KeyError: ip = request.META["REMOTE_ADDR"] request.ip = ip return self.get_response(request)
Although the middleware is short, I still made sure to write some tests. I based these on my subclass of Django’s
SimpleTestCase, since no DB queries should be made. To generate example requests, I used Django’s
RequestFactory. The end result was this test case with one test for each branch in the middleware:
from django.http import HttpResponse from django.test import RequestFactory from db_buddy.core.middleware import CloudflareMiddleware from db_buddy.test import SimpleTestCase class CloudflareMiddlewareTests(SimpleTestCase): request_factory = RequestFactory() def setUp(self): def dummy_view(request): return HttpResponse() self.middleware = CloudflareMiddleware(dummy_view) def test_ip_from_remote_addr(self): request = self.request_factory.get("/", REMOTE_ADDR="188.8.131.52") self.middleware(request) assert request.ip == "184.108.40.206" def test_ip_from_cloudflare(self): request = self.request_factory.get( "/", REMOTE_ADDR="220.127.116.11", HTTP_CF_CONNECTING_IP="18.104.22.168" ) self.middleware(request) assert request.ip == "22.214.171.124"
With the middleware now built, I installed it in the
MIDDLEWARE setting and then implemented login rate limiting with django-ratelimit using
Improve your Django develompent experience with my new book.
One summary email a week, no spam, I pinky promise.
- Simple In-Memory Caching of Django Model Data With cachetools
- Efficient Reloading in Django’s Runserver With Watchman
- Introducing django-version-checks