Rate-limit the API

Cap /api/ at 100 requests per 15 min and login at 5 failures.

shieldfy/API-Security-Checklistgithub.com
Security Checklist
ReferenceView source ↗

From security-best-practices: express-rate-limit, a MUST.

Hammer /api/

Hold to flood past 100, then fast-forward the clock.

Press and hold, or hold Space.
No limiterBad
Served
0
Rejected
0
Ceiling
none

        
app.use('/api/', router)
limiter: 100 / 15 minGood
Served
0
Rejected
0
Resets in
15:00

        
standardHeaders: true, legacyHeaders: false

import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  message: 'Too many requests from this IP, please try again later.',
  standardHeaders: true,
  legacyHeaders: false,
});

app.use('/api/', limiter);

Guess the password

Guess, or run the attack. The real one is letmein42.

Global limit onlyBad

Account safe

Failed attempts: 0 of 100 allowed

    9,600 guesses/day
    authLimiter 5 / 15 minGood

    Account safe

    Counted attempts 0/5

      const authLimiter = rateLimit({
        windowMs: 15 * 60 * 1000,
        max: 5,
        skipSuccessfulRequests: true,
      });
      
      app.use('/api/auth/login', authLimiter);

      What the numbers buy

      Drag the sliders or set the list size.

      • No limiter (20 req/s)
      • Global only (100 / 15 min)
      • Login limiter (5 / 15 min)

      guesses/day = max × (1440 / windowMin)   // 5 × (1440 / 15) = 480

      Why it matters, pros and cons

      Why it matters

      Without a ceiling, one client can exhaust the server and guess at network speed.

      Pros

      • One middleware line per route.
      • Retry-After tells clients when to retry.
      • Successful sign-ins never count toward lockout.

      Cons

      • Memory store is per process; share it via Redis.
      • Per-IP keys punish NATs and miss botnets.
      • Behind a proxy, set trust proxy or everyone shares one IP.