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).
durationcan be atimedeltaobject, or a tuple of two separate durations (for user-based and IP-based rules).blockspecifies 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.middlewarefor custom templates, or error from loggerdjango.securityotherwise).
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:TrueBLACKLIST_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; requiresBLACKLIST_ENABLE; default:TrueBLACKLIST_TEMPLATE- name of a custom error template to render to blocked clients; its context will containrequestandexception; set toNoneto use the template for status 400; default:NoneBLACKLIST_LOGGING_ENABLE- whether blocked requests should be logged (honored only if a custom error template is configured); default:TrueBLACKLIST_ADDRESS_SOURCE- the source of client addresses; can be a key inrequest.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_blacklistcron jobs. Cache TTL handles expiration automatically. - No database migrations needed. You can drop the
blacklist_ruletable 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3ddd3668c513bf2c842f524d8204f424b29db77bd1cf1eb5d0c23ff4a315395
|
|
| MD5 |
fb737bfdbc01011037ab79f5a8f12311
|
|
| BLAKE2b-256 |
1415263761c8df4928252dd9d1679b62b90bd11c38dba63057b3a922677718a3
|
File details
Details for the file django_blacklist_cache-1.0.0-py3-none-any.whl.
File metadata
- Download URL: django_blacklist_cache-1.0.0-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
087a72b91d6ca91c501f59b990567b11fa482beaf652b153b98236d5c60c4de9
|
|
| MD5 |
e182bdd1a6dad342e7aa441691c0eae6
|
|
| BLAKE2b-256 |
26433657bbf088941846676f3e53ea89a2e7671a6a2f706610f017be894dff6e
|