← Назад к вопросам
Как реализовать rate limiting для защиты Node.js API от злоупотреблений?
2.0 Middle🔥 231 комментариев
#Безопасность#Кэширование и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как реализовать rate limiting для защиты Node.js API от злоупотреблений?
Rate limiting — это ограничение количества запросов, которые клиент может сделать за определённый период. Это защита от DDoS атак, brute force и избыточного использования ресурсов.
Простой пример с express-rate-limit
import express from 'express';
import rateLimit from 'express-rate-limit';
const app = express();
// Базовый rate limiter: 100 запросов за 15 минут
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Лимит запросов
message: 'Too many requests, please try again later',
standardHeaders: true, // Вернуть информацию в RateLimit-* headers
legacyHeaders: false // Отключить X-RateLimit-* headers
});
// Применить ко всем запросам
app.use(limiter);
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});
Разные лимиты для разных endpoint'ов
const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
});
const strictLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5 // Строгий лимит для login
});
const apiLimiter = rateLimit({
windowMs: 1 * 60 * 1000,
max: 30 // Более мягкий для публичного API
});
app.use(globalLimiter); // Все запросы
app.post('/login', strictLimiter, (req, res) => {}); // Login
app.get('/api/data', apiLimiter, (req, res) => {}); // API
Rate limiting по IP и User ID
const userLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
keyGenerator: (req) => {
// Лимитировать по User ID если аутентифицирован
return req.user?.id || req.ip;
},
skip: (req) => {
// Не лимитировать админов
return req.user?.role === 'admin';
}
});
app.use(userLimiter);
Redis store для распределённых систем
import RedisStore from 'rate-limit-redis';
import redis from 'redis';
const redisClient = redis.createClient();
const limiter = rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'rl:' // Rate limit prefix
}),
windowMs: 15 * 60 * 1000,
max: 100
});
app.use(limiter);
// Теперь лимиты работают МЕЖДУ серверами!
Кастомный rate limiter с Redis
import redis from 'redis';
const redisClient = redis.createClient();
async function checkRateLimit(
key: string,
limit: number,
windowMs: number
): Promise<{ allowed: boolean; remaining: number; resetTime: number }> {
const current = await redisClient.incr(key);
if (current === 1) {
// Первый запрос - установить TTL
await redisClient.expire(key, Math.ceil(windowMs / 1000));
}
const ttl = await redisClient.ttl(key);
const allowed = current <= limit;
const remaining = Math.max(0, limit - current);
const resetTime = Date.now() + (ttl * 1000);
return {
allowed,
remaining,
resetTime
};
}
app.use(async (req, res, next) => {
const key = `rate-limit:${req.ip}`;
const result = await checkRateLimit(key, 100, 15 * 60 * 1000);
res.set('RateLimit-Limit', '100');
res.set('RateLimit-Remaining', result.remaining.toString());
res.set('RateLimit-Reset', (result.resetTime / 1000).toString());
if (!result.allowed) {
return res.status(429).json({
error: 'Too many requests',
retryAfter: Math.ceil((result.resetTime - Date.now()) / 1000)
});
}
next();
});
Слайдирующее окно (Sliding Window)
// Более точный метод - удаляет старые запросы
async function slidingWindowRateLimit(
key: string,
limit: number,
windowMs: number
): Promise<boolean> {
const now = Date.now();
const windowStart = now - windowMs;
// Удалить старые записи
await redisClient.zremrangebyscore(key, 0, windowStart);
// Подсчитать запросы в окне
const count = await redisClient.zcard(key);
if (count < limit) {
// Добавить текущий запрос
await redisClient.zadd(key, now, `${now}-${Math.random()}`);
await redisClient.expire(key, Math.ceil(windowMs / 1000));
return true;
}
return false;
}
Обработка ошибок rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
handler: (req, res) => {
res.status(429).json({
error: {
message: 'Too many requests',
status: 429,
retryAfter: req.rateLimit.resetTime
? Math.ceil((req.rateLimit.resetTime - Date.now()) / 1000)
: 900 // 15 minutes
}
});
},
onLimitReached: (req, res, options) => {
// Логируем когда лимит превышен
console.warn(`Rate limit reached for IP: ${req.ip}`);
// Можем отправить алерт
}
});
Rate limiting по типам операций
const readLimiter = rateLimit({
windowMs: 1 * 60 * 1000,
max: 1000 // Много читаем
});
const writeLimiter = rateLimit({
windowMs: 1 * 60 * 1000,
max: 50 // Мало пишем (дороже)
});
const deleteLimiter = rateLimit({
windowMs: 1 * 60 * 1000,
max: 10 // Очень осторожно с удалениями
});
app.get('/api/data', readLimiter, (req, res) => {});
app.post('/api/data', writeLimiter, (req, res) => {});
app.delete('/api/data/:id', deleteLimiter, (req, res) => {});
Production-ready стратегия
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import redis from 'redis';
const redisClient = redis.createClient({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT
});
// Разные лимиты
const limits = {
public: rateLimit({
store: new RedisStore({ client: redisClient, prefix: 'rl:public:' }),
windowMs: 1 * 60 * 1000,
max: 100
}),
authenticated: rateLimit({
store: new RedisStore({ client: redisClient, prefix: 'rl:auth:' }),
windowMs: 1 * 60 * 1000,
max: 500,
keyGenerator: (req) => req.user?.id || req.ip
}),
login: rateLimit({
store: new RedisStore({ client: redisClient, prefix: 'rl:login:' }),
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true // Не считать успешные логины
}),
payment: rateLimit({
store: new RedisStore({ client: redisClient, prefix: 'rl:payment:' }),
windowMs: 24 * 60 * 60 * 1000,
max: 10 // 10 платежей в день
})
};
app.post('/login', limits.login, (req, res) => {});
app.use('/api/public', limits.public);
app.use('/api/user', limits.authenticated);
app.post('/api/payment', limits.payment, (req, res) => {});
Мониторинг rate limiting
setInterval(async () => {
const stats = await redisClient.keys('rl:*');
console.log(`Active rate limit keys: ${stats.length}`);
}, 60000);
Вывод
Rate limiting важен для:
- Защиты от DDoS — ограничить количество запросов
- Защиты от Brute Force — ограничить попытки логина
- Справедливого распределения — все клиенты имеют равный доступ
- Контроля затрат — API calls стоят денег
- Стабильности — предотвратить перегрузку сервера