rate-limiting

v2026.09.24

Rate limiting and throttling. Token bucket, sliding window, fixed window algorithms. Express rate limit, Spring rate limiting, Redis-based distributed rate limiting, and API quota management. USE WHEN: user mentions "rate limit", "throttle", "API quota", "too many requests", "429", "express-rate-limit", "sliding window", "token bucket" DO NOT USE FOR: circuit breaker patterns - use `resilience-patterns`; DDoS protection - use infrastructure/WAF solutions

GitHub
Install command
npx skhub add claude-dev-suite/rate-limiting
Markdown
SKILL.md

Rate Limiting

Express (express-rate-limit)

import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import { createClient } from 'redis';

const redis = createClient({ url: process.env.REDIS_URL });

// Global rate limit
const globalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  limit: 100,
  standardHeaders: 'draft-7',
  legacyHeaders: false,
  store: new RedisStore({ sendCommand: (...args) => redis.sendCommand(args) }),
  message: { error: 'Too many requests, try again later' },
});

app.use(globalLimiter);

// Stricter limit on auth endpoints
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  limit: 5,
  keyGenerator: (req) => req.body.email || req.ip, // By email, not just IP
  store: new RedisStore({ sendCommand: (...args) => redis.sendCommand(args) }),
});

app.use('/api/auth/login', authLimiter);

Redis Sliding Window (custom)

async function isRateLimited(key: string, limit: number, windowSec: number): Promise<boolean> {
  const now = Date.now();
  const windowStart = now - windowSec * 1000;

  const multi = redis.multi();
  multi.zRemRangeByScore(key, 0, windowStart);    // Remove old entries
  multi.zAdd(key, { score: now, value: `${now}` });
  multi.zCard(key);                                  // Count in window
  multi.expire(key, windowSec);

  const results = await multi.exec();
  const count = results![2] as number;
  return count > limit;
}

// Usage
if (await isRateLimited(`rl:${userId}`, 100, 60)) {
  return res.status(429).json({ error: 'Rate limit exceeded' });
}

Algorithm Comparison

AlgorithmPrecisionMemoryBest For
Fixed windowLow (burst at edges)Very lowSimple APIs
Sliding window logHighMediumAccurate limiting
Sliding window counterMediumLowBalance of accuracy & memory
Token bucketMediumLowBurst-friendly APIs

Spring Boot (Bucket4j)

@Bean
public FilterRegistrationBean<RateLimitFilter> rateLimitFilter() {
    var filter = new RateLimitFilter(
        Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(1)))
    );
    var registration = new FilterRegistrationBean<>(filter);
    registration.addUrlPatterns("/api/*");
    return registration;
}

Response Headers

// Standard rate limit headers (RFC draft-7)
res.set({
  'RateLimit-Limit': '100',
  'RateLimit-Remaining': `${remaining}`,
  'RateLimit-Reset': `${resetTimestamp}`,
  'Retry-After': `${retryAfterSeconds}`, // On 429 responses
});

Anti-Patterns

Anti-PatternFix
In-memory rate limiting in multi-serverUse Redis-backed store
Rate limit by IP onlyAlso limit by user ID or API key
No rate limit headers in responseSend RateLimit-* headers for client awareness
Same limit for all endpointsTighter limits on auth, webhooks, expensive ops
No Retry-After on 429Include Retry-After header

Production Checklist

  • Redis-backed rate limiting for distributed deployments
  • Per-endpoint rate limit configuration
  • Standard rate limit headers in responses
  • Retry-After header on 429 responses
  • Rate limit by user ID (not just IP)
  • Monitoring: rate limit hit frequency
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/security/rate-limiting

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1