DevOps

Почему не работает ограничение запросов в Nginx

Решение проблем с rate limiting в Nginx. Основные причины неработающего ограничения запросов и правильная настройка конфигурации.

Почему не работает ограничение запросов в Nginx? Я новичок в Nginx и пытаюсь настроить rate limiting для защиты своего VDS от перегрузки. В конфигурации ниже я указал лимит в 30 запросов в секунду, но при тестировании с помощью Python-скрипта сервер не блокирует избыточные запросы. В чём может быть проблема?

Конфигурация Nginx:

nginx
# Редирект с HTTP на HTTPS
server {
    listen 80;
    server_name example.ru www.example.ru;

    return 301 https://$host$request_uri;
}

limit_req_zone $http_your_auth_token zone=mylimit:10m rate=30r/s;

# HTTPS сервер
server {
    listen 443 ssl;
    server_name example.ru www.example.ru;

    # SSL сертификаты
    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.key;

    # Базовые SSL настройки
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Логи
    access_log /var/log/nginx/backend_access.log;
    error_log  /var/log/nginx/backend_error.log;

    location / {
        limit_req zone=mylimit burst=1 nodelay;

        proxy_pass http://service:8080;

        # Заголовки для backend
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Таймауты
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Тестовый Python-скрипт:

python
from requests import get
import warnings
from urllib3.exceptions import InsecureRequestWarning

# Отключаем предупреждения о небезопасных запросах
warnings.simplefilter('ignore', InsecureRequestWarning)

for i in range(1000):
    res = get("https://localhost/sight/", verify=False)
    print(res.status_code)

Если ваше ограничение запросов в Nginx не работает, несмотря на корректную конфигурацию, проблема обычно кроется в одной из частых ошибок настройки. Рассмотрим основные причины и решения.

Содержание

Основные причины неработающего rate limiting в Nginx

Nginx rate limiting часто не работает из-за нескольких типичных ошибок в конфигурации. В вашем случае проблема может быть связана с неправильным выбором ключа для ограничения, расположением директив или отсутствием обязательных параметров. Давайте разберем каждую детально.

Проблема с ключом зоны limit_req_zone

Главная ошибка в вашей конфигурации — использование $http_your_auth_token в качестве ключа для rate limiting:

nginx
limit_req_zone $http_your_auth_token zone=mylimit:10m rate=30r/s;

Проблема в том, что заголовок your_auth_token может отсутствовать в запросах:

  1. Тестовый скрипт не отправляет этот заголовок — ваш Python-код не включает кастомные заголовки
  2. Ключ должен быть уникальным для каждого клиента — без заголовка все запросы попадают в одну группу
  3. Вместо этого используйте $binary_remote_addr — это IP-адрес клиента в двоичном формате, который всегда присутствует:
nginx
# Правильный вариант
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=30r/s;

Для использования JWT токенов или других идентификаторов убедитесь, что заголовок всегда отправляется клиентами. Официальная документация Nginx рекомендует $binary_remote_addr для базового ограничения.

Неправильное расположение директив в location-блоке

В вашей конфигурации директива limit_req стоит после proxy_pass:

nginx
location / {
    limit_req zone=mylimit burst=1 nodelay;
    proxy_pass http://service:8080;
    ...
}

Это неправильный порядок. Nginx обрабатывает директивы последовательно, и proxy_pass немедленно перенаправляет запрос. Rate limiting должен проверяться до перенаправления:

nginx
location / {
    limit_req zone=mylimit burst=1 nodelay;
    proxy_pass http://service:8080;
    ...
}

Важно: Если вы используете try_files, разместите limit_req перед ним. Директивы обработки запросов должны идти в начале location-блока.

Отсутствие параметра nodelay при высоких нагрузках

В вашей конфигурации есть параметр nodelay, но при тестировании с 1000 запросами он критически важен. Без nodelay Nginx будет накапливать избыточные запросы в очереди и задерживать их вместо немедленного отклонения.

nginx
limit_req zone=mylimit burst=1 nodelay;

Как это работает:

  • Без nodelay: запросы ставятся в очередь (до burst), обрабатываются с задержкой
  • С nodelay: избыточные запросы отклоняются немедленно (код 429)

Для защиты от ботов и атак nodelay — обязательный параметр. Блог NGINX подчеркивает его важность для реальных сценариев.

Конфликты location-блоков и совпадение URL

Убедитесь, что ваш тестовый URL /sight/ точно совпадает с location-блоком. Небольшие различия могут привести к тому, что rate limiting не применится:

  1. Добавление/удаление слэша: /sight/sight/
  2. Наличие индексного файла: /sight может обрабатываться отдельно от /sight/
  3. Конфликт с другими location-блоками: более специфичные блоки имеют приоритет

Проверка в логах:

bash
tail -f /var/log/nginx/backend_access.log

Ищите записи о rate limiting (код 429) и проверяйте точный URL из запроса.

Необходимая перезагрузка Nginx после изменений

Важно помнить, что изменения в конфигурации Nginx требуют перезагрузки:

bash
sudo nginx -t  # проверка конфигурации
sudo systemctl reload nginx  # перезагрузка без разрыва соединений

Без этого изменения не применяются. По данным ServerFault, это одна из самых частых причин неработающих настроек.

Дополнительные проверки

  1. Проверка заголовка в запросах:

    bash
    curl -H "your_auth_token: test" https://example.ru/sight/
    

    Убедитесь, что заголовок приходит на бэкенд.

  2. Проверка логов на ошибки:

    bash
    grep -i "limit_req" /var/log/nginx/error.log
    
  3. Проверка зоны shared memory:

    bash
    sudo nginx -T 2>&1 | grep mylimit
    
  4. Тестирование с IP-адресом вместо localhost:

    python
    res = get("https://your_server_ip/sight/", verify=False, headers={'your_auth_token': 'test'})
    

Рабочий пример конфигурации

nginx
# Базовое ограничение по IP
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=30r/s;

server {
    listen 443 ssl;
    server_name example.ru;

    location /sight/ {
        # Применяем ограничение ДО проксирования
        limit_req zone=mylimit burst=5 nodelay;
        
        proxy_pass http://service:8080;
        
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Мониторинг rate limiting

Для отладки добавьте метрики в лог:

nginx
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" '
                'limit_status=$limit_status';

Затем анализируйте поле $limit_status в логах. При срабатывании rate limiting его значение будет LIMITED.

Заключение

Nginx rate limiting не работает чаще всего из-за:

  1. Неправильного ключа (используйте $binary_remote_addr)
  2. Некорректного расположения директив (limit_req перед proxy_pass)
  3. Отсутствия nodelay для немедленного отклонения
  4. Конфликта location-блоков
  5. Отсутствия перезагрузки Nginx

Для защиты VDS от перегрузки рекомендуется комбинировать IP-ограничение с аутентификацией через заголовки, но всегда тестируйте конфигурацию на реальном трафике и анализируйте логи.

Источники

  1. Официальная документация: ngx_http_limit_req_module
  2. Rate Limiting with NGINX – NGINX Community Blog
  3. Why is my nginx rate limiting not working? - Server Fault
  4. Nginx is not enforcing rate limits on client requests - DrDroid
  5. NGINX rate limitting by decoded values from JWT token - Stack Overflow
Авторы
Проверено модерацией
Модерация
Почему не работает ограничение запросов в Nginx