Ограничение запросов в Node.js и Express

Что такое rate limiting
Rate limiting — это механизм ограничения количества запросов от источника за фиксированный интервал времени. Короткая дефиниция: middleware, который отслеживает активность клиента и отклоняет избыточные запросы.
Ключевые термины в одной строке:
- Rate limiting — ограничение частоты запросов.
- Middleware — промежуточный обработчик запросов в Express.
- IP-адрес — идентификатор клиента в сетях IPv4/IPv6; может быть подменён прокси.
Зачем это нужно
- Защита от DoS/DDoS-походов и брутфорса.
- Снижение нагрузки на CPU/память и уменьшение расходов.
- Уменьшение влияния ботов и злоумышленников.
Быстрый план внедрения
- Создать проект Express.
- Установить пакет express-rate-limit.
- Добавить middleware с настройками лимита.
- Применить глобально или для отдельных роутов.
- Протестировать и мониторить.
Шаг 1: Подготовка окружения
Создайте директорию проекта и инициализируйте npm:
mkdir express-app
cd express-app
npm init -yУстановите зависимости:
npm install express express-rate-limitПримечание: локальные единицы измерения в конфигурациях времени чаще указывают в миллисекундах, но при описании можно использовать секунды для удобства.
Шаг 2: Базовый сервер Express
Создайте файл index.js в корне проекта и добавьте базовый сервер:
// index.js
const express = require('express')
const app = express()
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log(`App running on port ${port}`)
})Этот код создаёт приложение и запускает прослушивание на порту 3000 по умолчанию.
Шаг 3: Роуты
Создайте папку routes и файл routes/routes.js с простыми обработчиками:
// routes/routes.js
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => {
res.send({ message: 'Hello, this is a GET request' })
})
router.post('/add-demo', (req, res) => {
res.status(201).send({ message: 'Resource created successfully' })
})
router.put('/update-demo', (req, res) => {
res.status(201).send({ message: 'Resource updated sucessfully' })
})
module.exports = routerИмпортируйте маршруты в index.js и подключите их до app.listen:
// index.js
const routes = require('./routes/routes')
app.use(routes)Шаг 4: Внедрение rate limiting
Создайте папку middleware и файл middleware/rate-limiter.js:
// middleware/rate-limiter.js
const rateLimiter = require('express-rate-limit')
const limiter = rateLimiter({
max: 5,
windowMs: 10000, // 10 секунд в миллисекундах
message: 'You can\'t make any more requests at the moment. Try again later',
})
module.exports = limiterОбъяснение полей:
- max: максимальное число запросов за windowMs. Может быть числом или функцией, возвращающей число.
- windowMs: окно времени в миллисекундах.
- message: ответ, отправляемый при превышении лимита; может быть строкой или JSON-объектом.
Примените middleware глобально, поместив app.use(limiter) перед маршрутами:
app.use(limiter)
app.use(routes)Важно: при превышении лимита клиент получит HTTP 429 Too Many Requests.
Применение к отдельным маршрутам
Иногда нужно отличать лимиты для разных точек входа, например для входа в систему.
Измените rate-limiter.js, чтобы экспортировать несколько конфигураций:
// middleware/rate-limiter.js
const rateLimiter = require('express-rate-limit')
const limiter = rateLimiter({
max: 5,
windowMs: 10000,
message: 'You can\'t make any more requests at the moment. Try again later',
})
const signInLimiter = rateLimiter({
max: 3,
windowMs: 10000, // 10 секунд
message: 'Too many sign-in attempts. Try again later.'
})
module.exports = {
limiter,
signInLimiter,
}Примените в роутере:
// routes/routes.js
const express = require('express')
const router = express.Router()
const { limiter, signInLimiter } = require('../middleware/rate-limiter')
router.get('/sign-in', signInLimiter, (req, res) => {
res.send({ message: 'Sign-in page' })
})
// Общий ограничитель для остальных роутов
router.use(limiter)
router.post('/post', (req, res) => {
res.status(201).send({ message: 'Resource created successfully' })
})
router.put('/put', (req, res) => {
res.status(201).send({ message: 'Resource updated sucessfully' })
})
module.exports = routerПорядок middleware в Express имеет значение: специфичные обработчики подключаются перед общими.
Когда встроенный подход не сработает
Counterexamples и ограничения метода отслеживания по IP:
- Клиенты за NAT или корпоративным прокси будут иметь одинаковый IP — лимит может блокировать многих легитимных пользователей.
- При распределённых атаках (DDoS) с множества IP простой rate limiter на одном сервере может быть недостаточен.
- Злоупотребление заголовком X-Forwarded-For может подделать IP, если прокси не настроен доверенным образом.
В таких случаях нужны более сложные решения: балансировка нагрузки, CDN/Edge rate limiting, распределённое хранилище счётчиков (Redis), Web Application Firewall.
Альтернативные алгоритмы ограничения
Короткие описания популярных алгоритмов:
- Fixed window: счётчик перезапускается по фиксированному окну (например, каждую минуту). Прост, но уязвим к пики в границе окон.
- Sliding window: вычисляет лимит в скользящем окне для более равномерного контроля.
- Token bucket: клиент хранит токены; запрос потребляет токен; позволяет короткие всплески.
- Leaky bucket: очередь запросов с постоянной отдачей; гладит пики.
Выбор зависит от требований к равномерности, допуску всплесков и сложности реализации.
Практические рекомендации и эвристики
- Для общих API начните с 5–100 запросов в минуту в зависимости от нагрузки.
- Для чувствительных операций (авторизация, смена пароля) выставляйте жёсткие лимиты, например 3-5 попыток в 10–60 секунд.
- Логируйте превышения лимита и собирайте метрики для настройки.
- На публичных сайтах используйте CDN/Edge rate limiting как первую линию защиты.
Фактические числа зависят от характера приложения и ожидаемой нагрузки.
Тестирование и критерии приёмки
Критерии приёмки:
- При нормальной нагрузке легитимные запросы не блокируются.
- При превышении лимита сервер возвращает 429 и понятное сообщение.
- Специфические лимиты для /sign-in действуют независимо от глобального лимита.
- Логи содержат записи о превышениях с метками времени и IP.
Минимальные тест-кейсы:
- Отправить меньше, чем max за окно — получить 200/201.
- Отправить max запросов ровно — все успешны.
- Отправить max+1 в пределах окна — последняя возвращает 429.
- Проверить, что ограничитель для /sign-in срабатывает раньше, чем глобальный.
- Проверить поведение при повторе после окончания окна.
Чеклист для ролей
Для разработчика:
- Добавить и покрыть тестами локальные лимиты.
- Логировать превышения и причины.
- Убедиться, что роуты с чувствительными операциями имеют отдельные лимиты.
Для DevOps:
- Мониторить метрики 429, latency и ошибки.
- Настроить механизм оповещений при всплесках 429.
- Рассмотреть использование Redis для распределённых счётчиков.
Для инженера по безопасности:
- Настроить доверенные прокси и корректную обработку X-Forwarded-For.
- Развернуть WAF/CDN-ограничения на уровне периметра.
Snippet: быстрая шпаргалка по настройке
// middleware/rate-limiter.js
const rateLimiter = require('express-rate-limit')
// Общий лимит
const limiter = rateLimiter({ max: 100, windowMs: 60_000, message: 'Too many requests' })
// Лимит для входа
const signInLimiter = rateLimiter({ max: 5, windowMs: 60_000, message: 'Too many sign-in attempts' })
module.exports = { limiter, signInLimiter }// index.js
const express = require('express')
const { limiter } = require('./middleware/rate-limiter')
const routes = require('./routes/routes')
const app = express()
app.use(limiter)
app.use(routes)Security hardening и приватность
- Не храните чувствительные данные в сообщениях об ошибках.
- Проверьте, что обработка заголовков прокси безопасна: используйте app.set(‘trust proxy’, true) только если доверяете прокси.
- Для GDPR: логирование IP считается персональными данными в ряде юрисдикций; храните и ретеншн логов в соответствии с политиками обработки данных.
Масштабирование: когда использовать Redis или другой бекенд
Локальный in-memory лимитер работает для одиночного инстанса, но при кластеризации:
- Используйте Redis или другой центральный стор для счётчиков, чтобы лимит работал на всех инстансах.
- Настройте TTL ключей по окну и выбирайте алгоритм, поддерживаемый выбранной библиотекой.
Decision flow для выбора стратегии
flowchart TD
A[Требуется rate limiting?] --> B{Нагружено многими инстансами}
B -- Нет --> C[Использовать in-memory limiter]
B -- Да --> D{Есть Redis/централизованное хранилище}
D -- Да --> E[Использовать распределённый счётчик 'Redis']
D -- Нет --> F[Использовать CDN/WAF на edge]
E --> G[Выбрать алгоритм: fixed/sliding/token/leaky]
C --> G
F --> GМодель зрелости (Maturity levels)
- Dev: простой in-memory limiter на одном инстансе.
- Test: маршруты с отдельными конфигурациями и логированием превышений.
- Prod-basic: Redis-backed счётчики, мониторинг 429.
- Prod-advanced: Edge rate limiting + WAF + автоматические блокировки по аномалиям.
Примеры ошибок и отладка
- Клиенты жалуются на блокировки — проверьте, нет ли у них общего NAT IP.
- Много 429 в логах — возможно, лимиты слишком жёсткие или окно слишком короткое.
- Неконсистентность в кластерных средах — убедитесь, что счётчики централизованы.
Резюме
Ограничение запросов — простой и эффективный инструмент для защиты вашего приложения. Начните с in-memory лимитера для разработки, добавьте экспортируемые конфигурации для чувствительных маршрутов и по мере роста приложения переходите на распределённые решения и edge-ограничения. Не забывайте про тестирование и мониторинг.
Важно
- Всегда проверяйте доверие к прокси и способ извлечения клиентского IP.
- Логи с IP — это персональные данные в ряде юрисдикций; согласуйте ретеншн с политиками конфиденциальности.
Краткая инструкция на 3 шага
- Установите express-rate-limit.
- Создайте limiter и signInLimiter в middleware.
- Подключите их в нужном порядке в вашем роутере.
Спасибо за внимание. Если нужно, могу подготовить готовый репозиторий с примерами Dockerfile и конфигурацией Redis для продакшн-сценария.
Похожие материалы
Facebook в iOS через джейлбрейк — Tweaks и руководство
Вернуть интервалы Проводника в Windows 11
Как сжать папку в Windows 10 и macOS
Как вывести звук с компьютера на телевизор
MSI Afterburner: мониторинг системы в играх