💻 Développement

dev-rate-limiter-designer

Conception de systèmes de rate limiting et throttling pour APIs — algorithmes, stockage distribué, headers standards, snippets copiables ASP.NET Core / Node / Redis.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-rate-limiter-designer --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-rate-limiter-designer -Launch"

🚀 Déjà installé ?

claude "/dev-rate-limiter-designer"

Ou tapez /dev-rate-limiter-designer dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

rate limitthrottlinglimite de requêtesAPI abuseDDoS protectionquota429 Too Many Requests

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-rate-limiter-designer ~/.claude/skills/

Payload du plugin : skills/dev-rate-limiter-designer · source éditable : dev-skills/rate-limiter-designer

📖 Manuel

Rate Limiter Designer

Workflow

1. Cartographier les surfaces à protéger

Identifier chaque endpoint par profil de risque :

ProfilExemplesLimite indicative
Authentification/login, /token, /forgot-password5 req/min/IP
Mutation sensible/pay, /transfer, /sms-otp10 req/min/user
Lecture lourde/search, /export, /report20 req/min/user
Read standard/products, /users/me300 req/min/user
Webhook entrant/webhook/*500 req/min/source IP

Distinguer les identités : IP (anonyme), API key (machine-to-machine), user ID (session).


2. Choisir l'algorithme selon le besoin

AlgorithmeAvantageInconvénientQuand l'utiliser
Fixed windowTrès simpleBurst doublé à la frontière de fenêtrePrototypage, admin interne
Sliding window logPrécision parfaiteRAM O(n) par clientHaute précision, faible trafic
Sliding window counterBon compromisLégère approximation (≤ fenêtre)API publique standard
Token bucketBurst contrôlé, filling régulierImplémentation plus complexeAPIs REST, cas général
Leaky bucketDébit constant, protection avalLatence artificielleFiles d'attente, systèmes fragiles en aval

Règle de décision rapide : si le client légitime a besoin de pics courts → token bucket ; si le système aval ne tolère pas les pics → leaky bucket ; si la simplicité prime → sliding window counter.


3. Définir les tiers et quotas

# config/rate-limits.yaml
tiers:
  anonymous:  { rpm: 10,   burst: 15 }
  free:       { rpm: 100,  burst: 150 }
  pro:        { rpm: 1000, burst: 1500 }
  enterprise: { rpm: 10000, burst: 15000 }

endpoints:
  /auth/login:         { rpm: 5,  scope: ip }
  /auth/forgot:        { rpm: 3,  scope: ip }
  /api/pay:            { rpm: 10, scope: user }
  /api/export:         { rpm: 5,  scope: user, cost: 10 }  # coût variable

4. Implémenter — snippets copiables

ASP.NET Core 8+ (built-in RateLimiter)

// Program.cs
builder.Services.AddRateLimiter(opt =>
{
    opt.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(ctx =>
    {
        var key = ctx.User.Identity?.Name ?? ctx.Connection.RemoteIpAddress?.ToString() ?? "anon";
        return RateLimitPartition.GetSlidingWindowLimiter(key, _ => new SlidingWindowRateLimiterOptions
        {
            PermitLimit        = 100,
            Window             = TimeSpan.FromMinutes(1),
            SegmentsPerWindow  = 6,
            QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
            QueueLimit         = 0
        });
    });
    opt.OnRejected = async (ctx, ct) =>
    {
        ctx.HttpContext.Response.StatusCode = 429;
        ctx.HttpContext.Response.Headers.RetryAfter = "60";
        await ctx.HttpContext.Response.WriteAsJsonAsync(new
        {
            error = "rate_limit_exceeded",
            retry_after = 60
        }, ct);
    };
});
// Appliquer : app.UseRateLimiter();

Node.js / Express (express-rate-limit + Redis)

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

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

export const apiLimiter = rateLimit({
  windowMs: 60_000,
  max: 100,
  standardHeaders: 'draft-7',   // RateLimit-* headers RFC 6585
  legacyHeaders: false,
  store: new RedisStore({ sendCommand: (...args) => redis.sendCommand(args) }),
  keyGenerator: (req) => req.user?.id ?? req.ip,
  handler: (req, res) => res.status(429).json({
    error: 'rate_limit_exceeded',
    retry_after: Math.ceil(res.getHeader('RateLimit-Reset') as number - Date.now() / 1000)
  })
});

Redis — Token Bucket en Lua (atomique)

-- token_bucket.lua  (KEYS[1]=bucket_key ARGV[1]=capacity ARGV[2]=refill_rate ARGV[3]=now_ms)
local key      = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate     = tonumber(ARGV[2])  -- tokens/seconde
local now      = tonumber(ARGV[3])

local bucket   = redis.call('HMGET', key, 'tokens', 'last_refill')
local tokens   = tonumber(bucket[1]) or capacity
local last     = tonumber(bucket[2]) or now

local elapsed  = (now - last) / 1000
tokens = math.min(capacity, tokens + elapsed * rate)

if tokens >= 1 then
  tokens = tokens - 1
  redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now)
  redis.call('EXPIRE', key, math.ceil(capacity / rate) + 10)
  return 1   -- OK
else
  return 0   -- rejeté
end

Appel Go/Node/C# : EVALSHA <sha> 1 user:42 100 1.67 <epoch_ms>.


5. Headers de réponse standards

HTTP/1.1 200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 43
RateLimit-Reset: 1719230460
RateLimit-Policy: 100;w=60

# Sur 429 :
HTTP/1.1 429 Too Many Requests
Retry-After: 17
Content-Type: application/json

Utiliser RFC 9110 Retry-After (secondes) sur tous les 429. Éviter d'exposer les compteurs précis sur les endpoints d'auth (aide les attaquants à calibrer leurs bots).


6. Rate limiting distribué (multi-instance / multi-région)

// Upstash Redis (Edge / Vercel / CF Workers)
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(100, "1 m"),
  analytics: true,
});

const { success, remaining, reset } = await ratelimit.limit(userId);
if (!success) return new Response("Too Many Requests", { status: 429 });

7. Monitoring et observabilité


Anti-patterns et pièges

PiègeConséquenceCorrection
Compter par IP derrière un load balancer sans X-Forwarded-ForTous les clients partagent la même IP (celle du LB)Lire X-Forwarded-For ou CF-Connecting-IP ; configurer trust proxy
Fixed window sans décalage aléatoireThundering herd à chaque resetSliding window ou jitter sur le reset
Pas de Retry-After sur 429Les clients retentent immédiatement, amplifiant la chargeToujours retourner Retry-After
Même limite pour toutes les routesEndpoint léger et endpoint coûteux identiquesCoût variable (cost-per-request) ou limites par endpoint
In-memory sur instances multiplesChaque instance a son propre compteur → 10× la limite réelleRedis partagé obligatoire dès la deuxième instance
Rate limiting sans authL'attaquant tourne les IPs librementCoupler auth + rate limiting ; CAPTCHA sur les endpoints publics sensibles
Limites trop strictes sans whitelistBlocage des crawlers légitimes, monitoring, CI/CDAPI keys internes exemptées, whitelist CIDR

Bonnes pratiques 2026