2 releases
Uses new Rust 2024
0.1.1 | Jun 20, 2025 |
---|---|
0.1.0 | Jun 18, 2025 |
#35 in Security
307 downloads per month
39KB
288 lines
actix-web-ratelimit
A simple and highly customizable rate limiting middleware for actix-web 4.
Features
- actix-web 4 Compatible: Built specifically for actix-web 4
- Simple & Easy to Use: Minimal configuration required
- Expandable Store: easy to create your own store, In-Memory store and Redis store have been provided
- High Performance: Efficient sliding window algorithm
- Customizable: Custom client identification and rate limit exceeded handlers
- Thread Safe: Concurrent request handling with DashMap
Quick Start
Add this to your Cargo.toml
:
[dependencies]
actix-web-ratelimit = "0.1"
# Or, for Redis support
actix-web-ratelimit = { version = "0.1", features = ["redis"] }
Usage
Basic Usage with In-Memory Store
// Configure rate limiting: allow 3 requests per 10-second window
let config = RateLimitConfig::default().max_requests(3).window_secs(10);
// Create in-memory store for tracking request timestamps
let store = Arc::new(MemoryStore::new());
HttpServer::new(move || {
App::new()
// create and register the rate limit middleware
.wrap(RateLimit::new(config.clone(), store.clone()))
.route("/", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
Advanced Configuration
let store = Arc::new(MemoryStore::new());
let config = RateLimitConfig::default()
.max_requests(3)
.window_secs(10)
// Extract client identifier from req. It is IP (realip_remote_addr) by default.
.id(|req| {
req.headers()
.get("X-Client-Id")
.and_then(|h| h.to_str().ok())
.unwrap_or("anonymous")
.to_string()
})
// Custom handler for rate limit exceeded. It returns a 429 response by default.
.exceeded(|id, config, _req| {
HttpResponse::TooManyRequests().body(format!(
"429 caused: client-id: {}, limit: {}req/{:?}",
id, config.max_requests, config.window_secs
))
});
HttpServer::new(move || {
App::new()
.wrap(RateLimit::new(config.clone(), store.clone()))
.route("/", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
Redis Store
first set feature redis
enable:
actix-web-ratelimit = { version = "0.1", features = [ "redis" ] }
then you can use it:
let store = Arc::new(
RedisStore::new("redis://127.0.0.1/0")
.expect("Failed to connect to Redis")
// Custom prefix for Redis keys
.with_prefix("myapp:ratelimit:"),
);
let config = RateLimitConfig::default().max_requests(3).window_secs(10);
HttpServer::new(move || {
App::new()
.wrap(RateLimit::new(config.clone(), store.clone()))
.route("/", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
Configuration Options
RateLimitConfig
Method | Description | Default |
---|---|---|
max_requests(usize) |
Maximum requests per window | 10 |
window_secs(u64) |
Time window in seconds | 100 |
id(fn) |
Client identification function | IP address |
exceeded(fn) |
Rate limit exceeded handler | 429 response |
Storage Backends
MemoryStore
- Pros: Fast, no external dependencies
- Cons: Not distributed, data lost on restart
- Use case: Single instance applications
RedisStore (requires redis
feature)
- Pros: Distributed, persistent, scalable
- Cons: Requires Redis server
- Use case: Multi-instance applications
Algorithm
This middleware uses a sliding window algorithm:
- Extract client identifier from request
- Retrieve stored request timestamps for the client
- Remove expired timestamps outside the time window
- Check if remaining request count exceeds the limit
- If not exceeded, record new timestamp and allow request
- If exceeded, call the rate limit handler
Examples
Run the example:
cargo run --example simple
Then test the rate limiting:
# This should work
curl http://localhost:8080/
# Exceed rate limit by making many requests
for i in {1..5}; do echo "$(curl -s http://localhost:8080)\r"; done
Features
redis
: Enables Redis storage backend support
License
This project is licensed under the MIT License.
Dependencies
~17–28MB
~485K SLoC