Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Axum and Redis

The core stays independent of a web framework and async runtime. Optional features provide focused adapters without taking ownership of application lifecycle or trust policy.

Axum

Enable Axum integration together with the default in-memory Store:

[dependencies]
tower-rate-limiter = { version = "0.1", features = ["axum", "memory"] }

IpKeyExtractor can then read Axum’s ConnectInfo<SocketAddr>. The server must supply connection information when serving the router:

    axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await?;

See Axum with nested policies for the complete source.

If ConnectInfo is missing, IpKeyExtractor returns a key error with code peer_ip_unavailable. Enabling the feature alone is not enough; the server construction shown above is what inserts the peer address.

Forwarding headers are untrusted input. If the application sits behind a proxy, establish and test its proxy trust policy before producing a forwarded client address; the crate does not do this implicitly.

The trusted forwarded addresses example shows application-owned parsing for a deployment where Nginx strips client-provided forwarding headers and writes a trusted value. Copying the parser without the matching proxy configuration would allow clients to choose their own rate-limit identity.

Redis

Enable Redis when multiple processes need to share usage:

[dependencies]
tower-rate-limiter = { version = "0.1", default-features = false, features = ["redis", "runtime-tokio"] }

RedisStore accepts an already established redis::aio::MultiplexedConnection. The application continues to own URL parsing, connection setup, reconnection strategy, and shutdown.

The redis feature uses one MULTI/EXEC transaction to initialize the counter, increment it, and read its TTL. Use redis-lua in place of redis to perform the same fixed-window operation with Lua. Either implementation must be combined with runtime-tokio or runtime-smol. A missing or non-positive TTL is surfaced as a Store error rather than repaired implicitly.

Redis adds an rl: transport marker and the optional namespace after it receives the scoped key. Use a namespace to separate deployments or applications sharing one Redis database. Namespace is a transport concern; use distinct policy names for distinct rate-limit policies.

See Axum with Redis for complete connection setup, namespacing, a shared Store, and custom error responses.

Choosing a Store

RequirementMemoryStoreRedisStore
Single processyesyes
Counters shared across replicasnoyes
External service requirednoyes
Survives process restartnousually, subject to Redis persistence
Runtime dependency in the adapternoneTokio- or Smol-compatible Redis connection

Cloning MemoryStore shares its in-process state. Creating separate MemoryStore::new() values creates separate counter sets. With multiple application replicas, each in-memory Store enforces its own quota, so the effective aggregate allowance can grow with replica count.

Each cached entry expires with its fixed window. Moka treats the entry as absent after that point and eventually removes it through cache maintenance without a background task.