Skip to main content

Blacklist users and hosts in Django using the cache framework. Automatically blacklist rate-limited clients.

Project description

Django Blacklist Cache

Blacklist users and hosts in Django using the cache framework. Automatically blacklist rate-limited clients.

Fork of django-blacklist that replaces database storage with Django's cache framework, making it ideal for serverless environments (AWS Lambda, Zappa) where database connections are expensive and ephemeral.

Overview

Django Blacklist Cache allows you to automatically block clients that exceed request rate limits. Instead of storing blacklist rules in the database, rules are stored in the configured cache backend (Redis, Memcached, etc.) with automatic expiration via TTL.

Key advantages over the database-backed approach:

  • No database connections for blacklist operations
  • No migrations to run
  • Instant propagation across all application instances
  • Automatic expiration of rules (no cleanup commands needed)
  • Graceful degradation if the cache is unavailable, the site continues to work (no one gets blocked, but no errors either)

Installation

To install the package, run:

$ pip install django-blacklist-cache

Add the BlacklistMiddleware middleware after AuthenticationMiddleware:

MIDDLEWARE = [
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'blacklist.middleware.BlacklistMiddleware',
    ...
]

Configure a cache backend for the blacklist:

CACHES = {
    'default': {
        ...
    },
    'blacklist': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://localhost:6379/1',
    }
}

BLACKLIST_CACHE_ALIAS = 'blacklist'

That's it. No database migrations to run. Add 'blacklist' to INSTALLED_APPS only if you want to use the blacklist_clear management command.

Why Use This Instead of Rate Limiting Alone?

django-ratelimit blocks requests that exceed a rate within a time window (e.g., 50 requests/minute). Once the window resets, the client can make requests again. This means an abusive client can keep hammering your server at the rate limit boundary forever -- 50 requests, wait a minute, 50 more, repeat.

Django Blacklist Cache adds a ban period on top of rate limiting. When a client exceeds the rate limit, they are blocked for a configurable duration (e.g., 30 minutes) regardless of the rate-limit window resetting.

Without blacklist:
  Client → 50 reqs → blocked → waits 1 min → 50 reqs → blocked → waits 1 min → ...

With blacklist:
  Client → 50 reqs → blocked for 30 min → can't make ANY request for 30 min

This is especially important for:

  • Brute-force protection: an attacker can't just wait for the rate-limit window to reset and try again
  • Resource protection: abusive clients are fully cut off, freeing server resources
  • Escalating responses: you can set longer ban durations for more sensitive endpoints

Usage

Automatic Blacklisting

Clients can be blacklisted automatically after exceeding a request rate limit. This feature requires django-ratelimit.

First, rate-limit a view by applying the @ratelimit decorator. Make sure to set block=False. Then, blacklist rate-limited clients by adding the @blacklist_ratelimited decorator. Specify the blacklist duration. For example:

from datetime import timedelta
from django_ratelimit.decorators import ratelimit
from blacklist.ratelimit import blacklist_ratelimited

@ratelimit(key='user_or_ip', rate='50/m', block=False)
@blacklist_ratelimited(timedelta(minutes=30))
def index(request):
    ...

Rules take effect immediately across all application instances. If the request comes from an authenticated user, the rule will target that user. Otherwise, it will target their IP address.

@blacklist_ratelimited accepts two arguments: (duration, block=True).

  • duration can be a timedelta object, or a tuple of two separate durations (for user-based and IP-based rules).
  • block specifies if the request should be blocked immediately, or passed to the view.

When a request is blocked due to a matching rule:

  • Status 400 (bad request) is returned.
  • An error template is rendered. You can specify a custom one (see Settings below), or use the one for status 400.
  • A message is logged (warning from logger blacklist.middleware for custom templates, or error from logger django.security otherwise).

Python API

You can manage blacklist rules programmatically:

from datetime import timedelta
from blacklist import block_ip, block_user, unblock_ip, unblock_user, is_blocked_ip, is_blocked_user, clear_all

# Block
block_ip('203.0.113.45', duration=timedelta(hours=1))
block_user(42, duration=timedelta(minutes=30))

# Check
is_blocked_ip('203.0.113.45')  # True
is_blocked_user(42)             # True

# Unblock
unblock_ip('203.0.113.45')
unblock_user(42)

# Clear all rules (uses cache.clear() on the blacklist cache alias)
clear_all()

Management Command

To use the management command, add 'blacklist' to INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    'blacklist',
]

Clear all blacklist rules:

$ python manage.py blacklist_clear

Remove a specific IP or user:

$ python manage.py blacklist_clear --ip 203.0.113.45
$ python manage.py blacklist_clear --user 42

Proxies and Client Addresses

By default, the client IP address is taken from the REMOTE_ADDR value of request.META. If your application server is behind one or more reverse proxies, this will usually be the address of the nearest proxy, and not the actual client address. To properly blacklist clients by IP address, you can configure Django Blacklist Cache to use addresses from another source (see Settings below).

To actually obtain the proxied client addresses, you can use django-ipware. In this case, you can configure Django Blacklist Cache to obtain client addresses from your function, which in turn calls django-ipware for the actual logic.

Alternatively, you can set REMOTE_ADDR from the X-Forwarded-For header in middleware, installed before Django Blacklist Cache. However, keep in mind that this header can be forged to bypass the rate limits. To counter that, you can use the last address in that header (which should be set by your trusted reverse proxy). If you are behind two proxies, use the second to last address, and so on.

Settings

  • BLACKLIST_ENABLE - whether blacklisted clients should be blocked, and rate-limited clients should be blacklisted; default: True
  • BLACKLIST_CACHE_ALIAS - the Django cache alias to use for storing blacklist rules; default: 'default'
  • BLACKLIST_RATELIMITED_ENABLE - whether rate-limited clients should be automatically blacklisted; requires BLACKLIST_ENABLE; default: True
  • BLACKLIST_TEMPLATE - name of a custom error template to render to blocked clients; its context will contain request and exception; set to None to use the template for status 400; default: None
  • BLACKLIST_LOGGING_ENABLE - whether blocked requests should be logged (honored only if a custom error template is configured); default: True
  • BLACKLIST_ADDRESS_SOURCE - the source of client addresses; can be a key in request.META, a callable that receives the request object, or the dotted string path to such a callable; default: 'REMOTE_ADDR'

Migrating from django-blacklist

This package is a fork of django-blacklist that replaces database storage with Django's cache framework. The Python import name (blacklist) is the same, so your existing decorator usage (@blacklist_ratelimited) works without code changes.

Step 1: Replace the package

$ pip uninstall django-blacklist
$ pip install django-blacklist-cache

Step 2: Update settings.py

# Add a cache backend for the blacklist (recommended: dedicated alias)
CACHES = {
    'default': {
        ...
    },
    'blacklist': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://localhost:6379/1',
    }
}

# Point the blacklist to your cache alias
BLACKLIST_CACHE_ALIAS = 'blacklist'

# Remove this setting (no longer used)
# BLACKLIST_RELOAD_PERIOD = 60  <-- delete this line

All other settings (BLACKLIST_ENABLE, BLACKLIST_RATELIMITED_ENABLE, BLACKLIST_TEMPLATE, BLACKLIST_LOGGING_ENABLE, BLACKLIST_ADDRESS_SOURCE) remain the same.

Step 3: Clean up

  • Remove any trim_blacklist cron jobs. Cache TTL handles expiration automatically.
  • No database migrations needed. You can drop the blacklist_rule table if desired:
    DROP TABLE IF EXISTS blacklist_rule;
    

What changed

django-blacklist django-blacklist-cache
Storage Database (PostgreSQL, etc.) Django cache (Redis, Memcached, etc.)
Expiration Manual (trim_blacklist command) Automatic (cache TTL)
Propagation Periodic reload (default: 60s) Instant across all instances
DB connections Required None
Django Admin Yes (CRUD for rules) No
CIDR network rules Yes No (individual IPs only)
INSTALLED_APPS Required Only for management command
Migrations Required Not needed

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_blacklist_cache-1.0.0.tar.gz (12.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_blacklist_cache-1.0.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file django_blacklist_cache-1.0.0.tar.gz.

File metadata

  • Download URL: django_blacklist_cache-1.0.0.tar.gz
  • Upload date:
  • Size: 12.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for django_blacklist_cache-1.0.0.tar.gz
Algorithm Hash digest
SHA256 b3ddd3668c513bf2c842f524d8204f424b29db77bd1cf1eb5d0c23ff4a315395
MD5 fb737bfdbc01011037ab79f5a8f12311
BLAKE2b-256 1415263761c8df4928252dd9d1679b62b90bd11c38dba63057b3a922677718a3

See more details on using hashes here.

File details

Details for the file django_blacklist_cache-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_blacklist_cache-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 087a72b91d6ca91c501f59b990567b11fa482beaf652b153b98236d5c60c4de9
MD5 e182bdd1a6dad342e7aa441691c0eae6
BLAKE2b-256 26433657bbf088941846676f3e53ea89a2e7671a6a2f706610f017be894dff6e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page