ratelimit
Rate limiting for Rust — GCRA (Generic Cell Rate Algorithm) with pluggable backends (in-memory, Redis) and optional Tower layer integration.
Features
- GCRA algorithm — smooth, memory-efficient rate limiting
- In-memory backend via
DashMap(default) - Optional Redis backend for distributed deployments
- Tower
LayerwithX-RateLimit-*response headers - Configurable burst / token-bucket capacity
What is GCRA?
The Generic Cell Rate Algorithm treats each key as a leaky bucket. A request is allowed if the bucket has capacity; otherwise it is rejected. Tokens are replenished at a constant rate derived from the desired RPS/RPM.
allowed if: ema ≤ limit
where ema(t) = max(0, ema(t-1) - (t - t_last)) + 1
This yields the same behaviour as a token bucket but only stores the last
emission time — one Instant per key.
Quick Start
use ;
async
Presets
| Constructor | Rate | Burst |
|---|---|---|
Quota::per_second |
n / s | n |
Quota::per_minute |
n / 60 s | n |
Quota::per_hour |
n / 3600 s | n |
Override burst with .allow_burst(n).
Tower Integration
By default the layer keys requests by the client's socket address and
ignores X-Forwarded-For entirely (secure by default — a client-set XFF
header cannot mint fresh budgets).
use ;
use RateLimitLayer;
use ;
use SocketAddr;
let backend = new;
let layer = new;
let app = new
.route
.layer;
// Serve with connect info so the layer can see the peer address.
// Without it, requests are rejected with 503 (fail closed) unless a
// MissingClientPolicy::FallbackKey is configured.
let listener = bind.await?;
serve.await?;
Behind a proxy
If the service sits behind proxies you control, tell the layer which
peers it may believe, and how many header entries your infrastructure
appends in front of the direct peer (num_trusted_hops):
use ;
// internet → ALB (trusted) → nginx (trusted, direct peer) → app
// nginx appends the ALB's address, so XFF is "client, alb-ip";
// the default num_trusted_hops = 1 skips "alb-ip" and yields "client".
let layer = new
.with_client_ip;
Resolution walks the header RIGHT-TO-LEFT and never trusts entries left
of the resolved one, so client-spoofed XFF values are ignored. For a
single internet → nginx → app proxy, set num_trusted_hops: 0.
Platforms with a dedicated header (CF-Connecting-IP) can set
trusted_header; same trust rules apply.
Migrating from 0.3.0: if you were behind a proxy, you must configure
trusted_proxies (the old trust-XFF-unconditionally behavior is gone);
if your service is directly exposed, you need nothing — the default is
stricter and safe. Callers keying by API keys instead of IP can use
.with_key_extractor(...).
Comparison with governor
| ratelimit | governor | |
|---|---|---|
| Algorithm | GCRA (leaky bucket) | GCRA (leaky bucket) |
| Backends | In-memory, Redis | In-memory only |
| Tower layer | Optional | Built-in |
| Dependencies | Minimal | Jitter + parking_lot |
| Burst support | .allow_burst() |
Fixed per-quota |
License
Licensed under either of MIT or Apache-2.0 at your option.
Security
Threat model: THREAT-MODEL.md.
Performance
Measured hot-path SLOs and allocation profile: PERF-SLO.md. Benchmarks run in CI (non-gating regression visibility against the saved ci baseline).