XRateLimiter
The XRateLimiter class provides intelligent rate limiting with tiered identification, multiple storage backends, and privacy-preserving hashing. It automatically registers as global middleware and classifies requests by trust level: authenticated users (highest), IP addresses (moderate), or browser fingerprints (lowest).
Contents
Usage
Basic setup
import { X } from "@ozanarslan/corpus";
// Default: memory store, 120/60/20 limits per minute
new X.RateLimiter();
Custom limits
new X.RateLimiter({
limits: { u: 200, i: 100, f: 30 }, // 200 user, 100 IP, 30 fingerprint
windowMs: 60_000, // 1 minute window
});
Configuration
RateLimitConfig
| Option | Type | Default | Description |
|---|---|---|---|
| limits | Record<"u" | "i" | "f", number> |
{ u: 120, i: 60, f: 20 } |
Request limits per tier |
| windowMs | number |
60000 |
Time window in milliseconds |
| saltRotateMs | number |
86400000 |
Salt rotation interval (24h) |
| cleanProbability | number |
0.005 |
Chance (0-1) of cleanup per request |
| maxStoreSize | number |
50000 |
Force cleanup threshold |
| storeType | "memory" | "file" | "redis" | "custom" |
"memory" |
Storage backend |
| store | RateLimitStoreInterface | undefined |
undefined |
Required when storeType is redis or custom |
| storeDir | string |
os.tmpdir() |
Directory for file store |
| headerNames | object |
See below | Custom header names |
Default header names
{
limit: "RateLimit-Limit",
remaining: "RateLimit-Remaining",
reset: "RateLimit-Reset",
retryAfter: "Retry-After"
}
Storage Backends
Memory (default)
Fastest option. Resets on server restart. Best for single-instance deployments.
new X.RateLimiter({ storeType: "memory" });
File
Persistent across restarts. Good for single-instance deployments needing persistence.
new X.RateLimiter({
storeType: "file",
storeDir: "./data/rate-limits",
});
Redis
Distributed storage for multi-instance deployments. Requires a Redis client.
import { X } from "@ozanarslan/corpus";
// Optional peer dependency
import { createClient } from "redis";
const redis = createClient({ url: "redis://localhost:6379" });
await redis.connect();
new X.RateLimiter({
storeType: "redis",
store: new X.RateLimiterRedisStore(redis),
});
Custom
class CustomStore implements X.RateLimitStoreInterface {
// ...
}
new X.RateLimiter({
storeType: "custom",
store: new CustomStore(),
});
Identification Tiers
Requests are classified into tiers based on available identification:
| Tier | Prefix | Source | Trust | Default Limit |
|---|---|---|---|---|
| User | u: |
JWT token from Authorization header |
Highest | 120 |
| IP | i: |
CF-Connecting-IP, X-Real-IP, or X-Forwarded-For |
Moderate | 60 |
| Fingerprint | f: |
Hash of User-Agent, Accept-Language, Accept-Encoding | Lowest | 20 |
Identification order
- Extracts JWT from
Authorization: Bearer <token> - Falls back to IP address validation
- Falls back to browser fingerprint
Privacy features
- IP addresses are hashed with a rotating salt (prevents long-term tracking)
- Salt rotates every 24 hours by default
- Fingerprints use hashed combinations, not raw values
Headers
The middleware automatically sets rate limit headers on all responses:
| Header | Description |
|---|---|
| RateLimit-Limit | Maximum requests allowed in window |
| RateLimit-Remaining | Remaining requests in current window |
| RateLimit-Reset | Unix timestamp when window resets |
| Retry-After | Seconds to wait (only when limited) |
| Access-Control-Expose-Headers | Exposes rate limit headers to CORS |
Custom header names
new X.RateLimiter({
headerNames: {
limit: "X-RateLimit-Limit",
remaining: "X-RateLimit-Remaining",
reset: "X-RateLimit-Reset",
retryAfter: "Retry-After",
},
});