Skip to main content

Async rate limiter for FastAPI with Redis or in-memory backend and advanced proxy-aware security

Project description

fastapi‑easylimiter

GitHub stars GitHub forks GitHub issues GitHub license PyPI


An ASGI async rate-limiting middleware for FastAPI with Redis or in-memory caching, designed to handle auto-generated routes (e.g., FastAPI-Users) without decorators, for simplicity and performance.


Features

  • Async rate limiting
  • Optional temporary IP bans
    • Configurable threshold (default 10 violations)
    • Sliding 15-min offense window
    • 5-min ban on repeat abuse
  • Cache Backends
    • Redis (recommended for multi-instance deployments)
    • In-Memory (single worker dev)
  • Path-based rules
    • Supports multi-rule prefix matching
    • Global and per-route limits
  • Standard rate-limit headers
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • X-RateLimit-Reset
    • Retry-After on 429 responses
  • Proxy Aware
    • Uses 'X-Forwarded-For' only when the sender is trusted, hardened logic
    • Rejects spoofed XFF headers
    • Supports 'CF-Connecting-IP' from Cloudflare, verified against CF CIDRs, when enabled
      • Must be public not private IP range.
    • Fallback to ASGI scope["client"] if no trusted headers exist
  • Zero dependencies beyond Redis client
    • Starlette-style ASGI middleware
  • Custom responses
    • HTMLResponse for browser clients
    • JSONResponse for API clients
  • CRLF-injection–safe header parsing
  • Allows banning direct connections (no proxy/CF) for dev/testing

Installation

pip install fastapi-easylimiter

Usage

from fastapi import FastAPI
from fastapi_easylimiter import AsyncRedisBackend, InMemoryBackend, RateLimiterMiddleware
import redis.asyncio as redis_async

app = FastAPI()

REDIS_URL = "redis://localhost:6379/0"

# Redis backend (recommended for multi-instance deployments)
redis_client = redis_async.from_url(REDIS_URL, decode_responses=True)
backend = AsyncRedisBackend(
  redis_client,            # Redis pool/client
  fail_open=False,         # Allow requests if Redis is down
  key_prefix="ratelimit:", # Redis key prefix
  eval_timeout=30          # Eval timeout
  )

# Or for single-instance/local development:
# backend = InMemoryBackend()

rules = {
    "/": {"limit": 50, "period": 5},            # GLOBAL: 50 req/5sec per IP
    "/api/": {"limit": 10, "period": 1},        # API: 10 req/sec per IP
    "/api/users": {"limit": 30, "period": 60},  # USER ROUTES: 30 req/60sec per IP
}

app.add_middleware(
    RateLimiterMiddleware,
    rules=rules,
    backend=backend,
    trusted_proxies=None,     # OPTIONAL: your proxy IPs (Only set if behind a proxy such as nginx)
    cloudflare=False,         # OPTIONAL: enable CF-Connecting-IP (Only set when behind cloudflare)
    enable_bans=True,         # OPTIONAL: enable temporary bans
    ban_threshold=10,         # Violations before ban
    ban_duration=300,         # Ban length in seconds
    offense_ttl=600,         # Offense counting window
    ban_page="<p>Your IP has been temporarily banned.</p>",        # OPTIONAL custom HTML ban page
    rate_page="<p>Too many requests. Please try again later.</p>", # OPTIONAL custom HTML rate-limit page
    ban_direct=True           # ONLY SET WHEN RUNNING BAREMETAL NO CF AND NO PROXY
)

Example: /api/users/me matches /api/users and /api. If any rule is exceeded → 429 returned.


Redis Lua Script

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])

local count = redis.call("INCR", key)
local ttl = redis.call("TTL", key)

if ttl == -1 then
    redis.call("EXPIRE", key, window)
    ttl = window
end

local reset = now + ttl
local exceeded = count > limit and 1 or 0

return {count, reset, exceeded}

Redis Key Patterns

Full Key Pattern Example Purpose
ratelimit:rl:{client_ip}:{prefix} ratelimit:rl:203.0.113.5:/api Rate-limit counter per IP + route prefix
ratelimit:ban:{client_ip} ratelimit:ban:203.0.113.5 Temporary ban flag
ratelimit:offenses:{client_ip} ratelimit:offenses:203.0.113.5 Offense counter used for tracking

Middleware Parameters

Parameter Type Description
app ASGIApp FastAPI/ASGI app
rules dict { prefix: {"limit": int, "period": int} }
backend Redis or InMemory backend Rate-limit storage
trusted_proxies list[str] Proxies allowed to trust XFF headers
cloudflare bool Enable Cloudflare IP extraction
enable_bans bool Enable temporary IP bans
ban_threshold int Violations before ban
ban_duration int Ban length in seconds
offense_ttl int Offense counting window in seconds
ban_page str Custom HTML ban page
rate_page str Custom HTML rate-limit page
ban_direct bool Bypass all checks and ban directly

Screenshot

image image image image

Contributing

Contributions and forks are always welcome! Adapt, improve, or extend for your own needs.


Support

Buy Me a Coffee


Parts of this code were generated/assisted by AI (Claude, Grok).

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

fastapi_easylimiter-0.3.7.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

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

fastapi_easylimiter-0.3.7-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_easylimiter-0.3.7.tar.gz.

File metadata

  • Download URL: fastapi_easylimiter-0.3.7.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.1

File hashes

Hashes for fastapi_easylimiter-0.3.7.tar.gz
Algorithm Hash digest
SHA256 6a00ebbfb86a982d41b58eb5e9fbc16988afc6e659982585bcfdbc0c65e35732
MD5 addfe343a70699dba63c4a590494036b
BLAKE2b-256 e713a4ae75216a8f6bdbbddc530772a6b52bca3a346d12146d503a73ffc98489

See more details on using hashes here.

File details

Details for the file fastapi_easylimiter-0.3.7-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_easylimiter-0.3.7-py3-none-any.whl
Algorithm Hash digest
SHA256 94454c0e7c524b6f22e941a816028490f6334784cf99096d6b313d82fa7e90bb
MD5 e69ed08e7a740b2a65c55d78aab8437d
BLAKE2b-256 fd10f2ba0f19d574f44e2c51a13e1a2e1a6f9fdc0ff5e4e05a618daaca74a094

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