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

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

6 min read Backend Обновлено 06 Dec 2025
Rate limiting в Node.js и Express
Rate limiting в Node.js и Express

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

Что такое rate limiting

Rate limiting — это механизм ограничения количества запросов от источника за фиксированный интервал времени. Короткая дефиниция: middleware, который отслеживает активность клиента и отклоняет избыточные запросы.

Ключевые термины в одной строке:

  • Rate limiting — ограничение частоты запросов.
  • Middleware — промежуточный обработчик запросов в Express.
  • IP-адрес — идентификатор клиента в сетях IPv4/IPv6; может быть подменён прокси.

Зачем это нужно

  • Защита от DoS/DDoS-походов и брутфорса.
  • Снижение нагрузки на CPU/память и уменьшение расходов.
  • Уменьшение влияния ботов и злоумышленников.

Быстрый план внедрения

  1. Создать проект Express.
  2. Установить пакет express-rate-limit.
  3. Добавить middleware с настройками лимита.
  4. Применить глобально или для отдельных роутов.
  5. Протестировать и мониторить.

Шаг 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.

Минимальные тест-кейсы:

  1. Отправить меньше, чем max за окно — получить 200/201.
  2. Отправить max запросов ровно — все успешны.
  3. Отправить max+1 в пределах окна — последняя возвращает 429.
  4. Проверить, что ограничитель для /sign-in срабатывает раньше, чем глобальный.
  5. Проверить поведение при повторе после окончания окна.

Чеклист для ролей

Для разработчика:

  • Добавить и покрыть тестами локальные лимиты.
  • Логировать превышения и причины.
  • Убедиться, что роуты с чувствительными операциями имеют отдельные лимиты.

Для 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)

  1. Dev: простой in-memory limiter на одном инстансе.
  2. Test: маршруты с отдельными конфигурациями и логированием превышений.
  3. Prod-basic: Redis-backed счётчики, мониторинг 429.
  4. Prod-advanced: Edge rate limiting + WAF + автоматические блокировки по аномалиям.

Примеры ошибок и отладка

  • Клиенты жалуются на блокировки — проверьте, нет ли у них общего NAT IP.
  • Много 429 в логах — возможно, лимиты слишком жёсткие или окно слишком короткое.
  • Неконсистентность в кластерных средах — убедитесь, что счётчики централизованы.

Резюме

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

Важно

  • Всегда проверяйте доверие к прокси и способ извлечения клиентского IP.
  • Логи с IP — это персональные данные в ряде юрисдикций; согласуйте ретеншн с политиками конфиденциальности.

Краткая инструкция на 3 шага

  1. Установите express-rate-limit.
  2. Создайте limiter и signInLimiter в middleware.
  3. Подключите их в нужном порядке в вашем роутере.

Спасибо за внимание. Если нужно, могу подготовить готовый репозиторий с примерами Dockerfile и конфигурацией Redis для продакшн-сценария.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

Facebook в iOS через джейлбрейк — Tweaks и руководство
iOS

Facebook в iOS через джейлбрейк — Tweaks и руководство

Вернуть интервалы Проводника в Windows 11
Windows 11

Вернуть интервалы Проводника в Windows 11

Как сжать папку в Windows 10 и macOS
Инструкции

Как сжать папку в Windows 10 и macOS

Как вывести звук с компьютера на телевизор
Руководство

Как вывести звук с компьютера на телевизор

MSI Afterburner: мониторинг системы в играх
Гайды

MSI Afterburner: мониторинг системы в играх

Микродергание в играх: причины и исправления
Оптимизация игр

Микродергание в играх: причины и исправления