Гид по технологиям

Ограничение запросов (rate limiting) в Express

6 min read Backend Обновлено 04 Jan 2026
Rate limiting в Express — быстрое руководство
Rate limiting в Express — быстрое руководство

Человек, держащий наклейку Node.js

Краткое введение

Ограничение частоты запросов — это стратегия управления трафиком. Оно ограничивает число запросов от одного клиента за заданный промежуток времени. Существуют разные алгоритмы (фиксированное окно, скользящее окно, токен-ведро и др.), у каждого свои плюсы и минусы. В примерах ниже мы используем простой и распространённый подход с middleware для Express.

Важно: в документации и в сообществе встречаются разные названия и варианты конфигурации. Здесь показан практический путь, который можно расширять.

Что вам понадобится

  • Node.js и npm
  • Базовое приложение на Express
  • Пакет express-rate-limit

Термины: rate limiting — ограничение частоты запросов; middleware — промежуточная функция Express, которая обрабатывает запросы до маршрутов.

Шаг 1: Создание окружения разработки

Создайте каталог проекта и инициализируйте npm:

mkdir express-app  

Зайдите в каталог:

cd express-app  

Инициализируйте package.json:

npm init -y  

Установите зависимости:

npm install express express-rate-limit

Пояснение: express — веб-фреймворк для Node.js. express-rate-limit — middleware, ограничивающий число повторяющихся запросов к публичным API и конечным точкам.

Шаг 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}`);  
});  

Этот код импортирует express, создаёт приложение и запускает прослушивание на порту 3000 (или значении из переменной среды PORT).

Шаг 3: Создание обработчиков маршрутов

Создайте папку routes и файл routes/routes.js (имя файла в примере — routes.js внутри папки routes). Пример содержимого:

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 и используйте как middleware (до app.listen):

// index.js  
const routes = require("./routes/routes");  

и

// index.js  
app.use(routes);  

Убедитесь, что подключение маршрутов расположено перед вызовом app.listen.

Шаг 4: Реализация rate limiting

Создайте папку middleware и файл middleware/rate-limiter.js с таким содержимым:

// rate-limiter.js  
const rateLimiter = require("express-rate-limit");  
  
const limiter = rateLimiter({  
    max: 5,  
    windowMS: 10000, // 10 seconds  
    message: "You can't make any more requests at the moment. Try again later",  
});  
  
module.exports = limiter  

Пояснение параметров в объекте конфигурации:

  • max — максимальное число запросов от одного клиента за окно. Обычно число или функция.
  • windowMS — продолжительность окна в миллисекундах (ms). В примере указано 10000 (10 секунд).
  • message — сообщение, отправляемое при превышении лимита. Может быть строкой, объектом JSON или любым значением, допустимым для res.send.

Поведение: при превышении лимита middleware вернёт статус 429 Too Many Requests.

Затем примените limiter глобально, подключив его в index.js выше маршрутов:

app.use(limiter);  

После этого все маршруты будут защищены общим лимитом.

Ограничение для отдельных маршрутов

Иногда нужно отдельное ограничение для чувствительных маршрутов (например, вход в аккаунт). В этом случае удалите глобальный limiter и экспортируйте несколько конфигураций:

const signInLimiter = rateLimiter({  
    max: 3,  
    windowMS: 10000, //10 seconds  
    message: "Too many sign-in attempts. Try again later."  
})  
  
module.exports = {  
    limiter,  
    signInLimiter  
}  

Примените в роутере:

// router.js  
const express = require("express");  
const router = express.Router();  
const {limiter, signInLimiter} = require("../middleware/rate-limiter")  
  
router.get("/sign-in", signInLimiter, (req, res, next) => {  
  res.send({ message: "Hello, this is a GET request" });  
});  
  
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;  

Таким образом для маршрута /sign-in будет действовать более строгая политика, а для остальных маршрутов — общая.

Частые ошибки и на что обратить внимание

  • Параметры windowMS vs windowMs: в разных примерах используется разная капитализация. Проверьте документацию вашей версии express-rate-limit и используйте корректное имя параметра.
  • Глобальный limiter накладывается на все маршруты. Уберите глобальное подключение, если хотите комбинировать настроенные лимитеры для отдельных маршрутов.
  • IP-адрес как ключ: по умолчанию ключом считается IP клиента. Если у вас прокси или CDN (например, Nginx, Cloudflare), настройте trust proxy и/или используйте заголовки X-Forwarded-For.
  • Хранилище счётчиков: по умолчанию используется память процесса. Это не годится для кластеров/мультисерверных развёртываний. Для продакшна используйте внешнее хранилище (Redis, Memcached).

Альтернативные подходы и когда они подходят

  • Токен-ведро (token bucket): хорош для сглаживания всплесков трафика при необходимости равномерного распределения.
  • Скользящее окно (sliding window): уменьшает «стаковый» эффект фиксированных окон.
  • API Gateway / WAF: если у вас уже стоит API-шлюз (AWS API Gateway, Kong, Nginx, Cloudflare), имеет смысл реализовать лимиты на уровне инфраструктуры.
  • CAPTCHA для ограниченных маршрутов (например, повторяющиеся попытки входа).

Когда простого express-rate-limit не хватает:

  • У вас кластерная архитектура без общего хранилища — используйте Redis-backed store.
  • Атакуют на уровне сетевого стека (L3/L4) — нужны сетевые фильтры и WAF.

Модель мышления и эвристики

  • Начинайте с малого: разумный глобальный лимит + отдельные жёсткие лимиты для уязвимых маршрутов (вход, восстановление пароля).
  • Защитите ресурсоёмкие операции более жёстко.
  • Лимиты для анонимных пользователей обычно строже, чем для авторизованных.
  • Пользовательский опыт важен: возвращайте понятные сообщения и рекомендации (через Retry-After заголовок).

Фактбокс — ключевые числа

  • Пример в статье использует max = 5 и windowMS = 10000 (5 запросов за 10 секунд).
  • Часто по умолчанию используют 5 запросов на минуту или 100–1000 запросов в минуту в зависимости от API.
  • HTTP статус при превышении лимита: 429 Too Many Requests.

Безопасность и харднинг

  • Используйте общую базу для счётчиков (Redis) при масштабировании.
  • Установите trust proxy в Express, если запросы проходят через прокси: app.set(‘trust proxy’, 1).
  • Логируйте события с превышением лимита и интегрируйте с SIEM.
  • Для чувствительных маршрутов добавляйте вторую проверку (CAPTCHA, двухфакторная аутентификация).

Роли и чеклист (разработчик / SRE / безопасность)

Разработчик:

  • Внедрить express-rate-limit и локальные лимитеры.
  • Добавить информативные сообщения и заголовки Retry-After.

SRE / DevOps:

  • Подключить Redis/Memcached как store для счётчиков.
  • Настроить мониторинг и алерты на рост ошибок 429.

Специалист по безопасности:

  • Анализировать логи превышений лимитов и выявлять брутфорс.
  • Настроить политики на WAF и CDN.

Руководство — быстрый план действий (SOP)

  1. Инициализация проекта и установка зависимостей.
  2. Создание middleware/rate-limiter.js с базовой конфигурацией.
  3. Подключение limiter в index.js (локально или глобально).
  4. Для продакшна: подключить Redis Store и настроить trust proxy.
  5. Тестирование: функциональные тесты и нагрузочное тестирование.
  6. Деплой и мониторинг.

Тест-кейсы и критерии приёмки

  • Кейс 1: Клиент делает 5 запросов в течение окна — все проходят. Шестой — возвращает 429.
  • Кейс 2: Для маршрута /sign-in сделано 3 запроса в окне — четвёртый возвращает 429.
  • Кейс 3: При наличии Redis-сторе счётчики синхронизируются между инстансами.

Критерии приёмки:

  • Для общего набора запросов поведение соответствует конфигурации limiter.
  • 429 ошибки логируются и имеют читаемое сообщение и заголовок Retry-After (если настроен).

Decision tree (Flowchart)

flowchart TD
  A[Запрос пришёл] --> B{Маршрут /sign-in?}
  B -- Да --> C[Применить signInLimiter]
  B -- Нет --> D{Глобальный limiter подключён?}
  D -- Да --> E[Применить limiter]
  D -- Нет --> F[Без ограничения]
  C --> G{Превышен лимит?}
  E --> G
  G -- Да --> H[Вернуть 429 и лог]
  G -- Нет --> I[Передать обработчику маршрута]

Когда ограничение не сработает или даст ложные срабатывания

  • Клиенты за NAT/прокси будут концернтироваться на одном IP — возможно ложное блокирование.
  • При неправильной конфигурации trust proxy — идентификация IP будет неверной.
  • По умолчанию счётчики в памяти не синхронизируются между инстансами — при масштабировании поведение неконсистентно.

Миграция и совместимость

  • При переходе на Redis store проверьте совместимость версии express-rate-limit и используемого store-модуля.
  • Тестируйте на staging перед продакшном.

Краткое резюме

Ограничение частоты запросов в Express — недорогой и эффективный способ снизить нагрузку, уменьшить влияние ботов и защититься от ряда атак. Для простых приложений подойдёт встроенный in-memory store, но для продакшна и кластерных развёртываний рекомендуется использовать общий стор (Redis). Для чувствительных маршрутов применяйте отдельные, более строгие лимитеры.

Важно: всегда тестируйте поведение в условиях, максимально приближённых к боевым.

Глоссарий (1 строка на термин)

  • Rate limiting — ограничение частоты запросов от клиента за заданный период.
  • Middleware — функция, обрабатывающая запрос до передачи в маршрут.
  • Store — хранилище счётчиков лимитирования (память, Redis и т.д.).
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство