Skip to content
← Все статьи

API Rate Limiting: token bucket, 429, Retry-After

API Rate Limiting: token bucket, 429, Retry-After

Rate limiting — это ограничение частоты запросов к API документацию на одного клиента. Цели: предотвратить брутфорс auth-эндпоинтов, защитить от DDoS, сохранить инфраструктуру от злоупотребления тяжёлыми запросами, справедливо распределить ресурсы между тарифы. В статье разберём алгоритмы (token bucket, sliding window), HTTP 429 + Retry-After и реализации на Redis, nginx, Express.

Зачем rate limiting

Алгоритмы

Fixed Window

Счётчик сбрасывается каждую минуту/час. Плюсы: просто. Минусы: «граничный burst» — 100 запросов в 23:59 и ещё 100 в 00:00 = 200 за 2 секунды.

Sliding Window

Учитывает скользящее окно последних N секунд. Точнее, но дороже по памяти. Реализуется через Redis sorted set с timestamp'ами:

ZADD ratelimit:user:123 {now} {now}
ZREMRANGEBYSCORE ratelimit:user:123 0 {now - 60}
ZCARD ratelimit:user:123   ← текущее количество за последние 60 сек
EXPIRE ratelimit:user:123 120

Token Bucket

Бакет вмещает N токенов, пополняется со скоростью M/сек. Каждый запрос забирает 1 токен. Если пусто — 429. Хорошо работает для burst-трафика (кэш-прогрев, массовую проверку URL-запрос).

Leaky Bucket

Обратный к token bucket: запросы заливаются в очередь фиксированной скоростью. Smooth traffic, но не даёт burst'ов.

HTTP 429 и Retry-After

При превышении лимита возвращайте:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705324800
Content-Type: application/json

{"error":"rate_limit_exceeded","message":"Try again in 30 seconds"}

Retry-After — в секундах или как HTTP-дата. Клиенты (curl, браузеры) умеют уважать этот заголовок с backoff.

Реализация на Redis (PHP)

function checkRateLimit(Redis $r, string $key, int $max, int $window): bool {
    $now = microtime(true);
    $r->zAdd("rl:{$key}", $now, "{$now}");
    $r->zRemRangeByScore("rl:{$key}", 0, $now - $window);
    $count = $r->zCard("rl:{$key}");
    $r->expire("rl:{$key}", $window * 2);
    return $count <= $max;
}

if (!checkRateLimit($redis, "user:{$userId}", 60, 60)) {
    header('HTTP/1.1 429 Too Many Requests');
    header('Retry-After: 60');
    exit(json_encode(['error' => 'rate_limit_exceeded']));
}

nginx

limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

server {
    location /api/ {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://backend;
    }
    location /api/auth/login {
        limit_req zone=login burst=3 nodelay;
        proxy_pass http://backend;
    }
}

Express / NestJS

// Express
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

app.use('/api/', rateLimit({
  store: new RedisStore({ client: redisClient }),
  windowMs: 60_000,
  max: 60,
  standardHeaders: 'draft-7',
  legacyHeaders: false,
  keyGenerator: (req) => req.user?.id || req.ip,
}));

// NestJS
ThrottlerModule.forRoot({
  throttlers: [{ ttl: 60_000, limit: 60 }],
  storage: new RedisThrottlerStorage(redis),
})

Ключ rate limit: IP vs user vs API key

Лучшая практика — комбинация: user_id || api_key || ip.

Auth-эндпоинты: особые правила

Login/register/password-reset: максимум 5 попыток в 15 минут per IP + per email. Используйте задержку bcrypt (cost 12+), добавьте CAPTCHA после 3 неудач.

Мониторинг

Следите за метриками: процент 429, топ-10 IP по 429, latency при rate limit. Рекомендую настроить мониторинг на rising 429 rate — это сигнал о DDoS или сломанном клиенте.

FAQ

Чем отличается 429 от 503? 429 — клиент превысил свой лимит, 503 — сервер перегружен независимо от клиента.

Нужен ли Retry-After? Очень желательно — без него клиенты делают busy-loop, ухудшая ситуацию.

Как защититься от распределённого DDoS? Rate limit на приложении не хватит — нужен Cloudflare/AWS Shield или WAF.

Можно ли обойти rate limit? Через ротацию IP (прокси), ротацию API-ключей, бесплатные триалы. Защита — фингерпринтинг + behavioural analysis.

Вывод

Минимум: sliding-window на Redis, 429+Retry-After, отдельный жёсткий лимит на auth-эндпоинты. Для серьёзных угроз — WAF. Мониторьте API через enterno monitors. Связанное: CORS, HTTP Security Headers.

Проверьте ваш сайт прямо сейчас

Проверить →
Другие статьи: SEC
SEC
CSP (Content Security Policy): настройка с нуля
15.04.2026 · 4 просм.
SEC
Subresource Integrity (SRI): защита CDN-скриптов
15.04.2026 · 5 просм.
SEC
WAF (Web Application Firewall): базовый гайд
15.04.2026 · 6 просм.
SEC
Защита от XSS атак: типы, эскейпинг, CSP, Trusted Types
15.04.2026 · 3 просм.