Главная/Статьи/Аномальная нагрузка на VPS: блокируем crawler на уровне nginx

Аномальная нагрузка на VPS: блокируем crawler на уровне nginx

Как Facebook crawler перегружал VPS и разгонял CPU до 60%. Настраиваем точечную блокировку тяжёлых URL каталога на уровне nginx.

ДМ
Дмитрий Мещеряков
📅 21 мая 2026 г.📖 6 мин чтения

Самая неприятная разновидность проблем с производительностью — та, где метрики выглядят плохо, а причина находится вне вашего кода. Профилировщик показывает, что каталог работает нормально. Запросы к базе укладываются в норму. Продажи не выросли. А CPU держится на шестидесяти процентах и периодически уводит сервер в ребут.

Разбор ниже — про то, как такая нагрузка ищется (в access-логах, а не в профилировщике) и почему блокировать её нужно на входе, а не в приложении.

Симптомы проблемы

На проекте 1С-Битрикс появилась проблема: VPS-сервер держал утилизацию CPU на уровне около 60%, а в отдельные периоды уходил выше и падал в ребут.

Всплески не были привязаны к:

  • Рекламной активности
  • Обновлениям каталога
  • Росту реального пользовательского трафика

Дополнительно рос объём системного кэша, а каталог товаров вёл себя как источник постоянной фоновой нагрузки.

Анализ логов

Первым шагом стал анализ логов nginx:

bash
awk -F\" '{print $6}' access.log | sort | uniq -c | sort -nr | head -50

В топе оказался:

text
115604 meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)

Значительная часть нагрузки связана не с пользователями, а с роботом Facebook, который активно обходит страницы сайта.

Что именно делал crawler

Проблема была не просто в количестве обращений. По логам crawler активно перебирал URL каталога:

  • Страницы разделов
  • Страницы со smart.filter
  • Страницы с пагинацией
  • URL с сортировками
  • URL с разными вариантами отображения

Каждый такой запрос запускал тяжёлую серверную обработку и порождал новые варианты кэша.

Почему блокировка в PHP — плохая идея

⚠️ Важно

При блокировке на уровне приложения:

  1. Запрос уже дошёл до nginx
  2. Затем до Apache или php-fpm
  3. Затем до Битрикса
  4. И только после этого сервер начал бы его отбрасывать

Нагрузка уже возникла. Защита должна стоять как можно раньше.

Стоимость запроса, отброшенного nginx, — микросекунды и ноль обращений к базе. Стоимость того же запроса, отброшенного в init.php, — полный старт ядра Битрикса: подключение модулей, соединение с MySQL, чтение настроек. Разница между этими двумя вариантами на потоке в сто тысяч запросов и есть те самые шестьдесят процентов CPU.

Решение: блокировка на уровне nginx

Оптимальная точка — nginx, который стоит перед приложением.

Преимущества:

  • Запрос отбрасывается до запуска PHP
  • Apache и Битрикс не участвуют в обработке
  • Не создаётся дополнительная нагрузка на CPU
  • Можно точно ограничить только опасные сценарии

Конфигурация nginx

Определение условий

nginx
# Определяем meta-externalagent
map $http_user_agent $is_meta_externalagent {
    default 0;
    ~*meta-externalagent 1;
}

# Определяем тяжёлые URL каталога
map $request_uri $is_heavy_catalog_url {
    default 0;
    ~^/catalog/.*/filter/ 1;
    ~^/catalog/.*(-from-|-to-) 1;
    ~^/catalog/.*[?&]PAGEN_[0-9]+= 1;
    ~^/catalog/.*[?&]sort= 1;
    ~^/catalog/.*[?&]display= 1;
    ~^/catalog/.*[?&]linerow= 1;
}

# Комбинируем условия
map "$is_meta_externalagent:$is_heavy_catalog_url" $block_meta_heavy_catalog {
    default 0;
    "1:1" 1;
}

Применение блокировки

nginx
server {
    listen 80;
    server_name example.com;
    
    # Блокируем crawler только на тяжёлых URL
    if ($block_meta_heavy_catalog) {
        return 403;
    }
    
    # ... остальная конфигурация
}

Почему не блокируем crawler целиком

Полная блокировка могла бы повлиять на корректность генерации превью ссылок в соцсети. Вместо этого выбрана точечная схема: ограничиваем именно тяжёлые URL каталога.

URLCrawlerРезультат
/meta-externalagent✅ Доступ
/catalog/category/meta-externalagent✅ Доступ
/catalog/category/?PAGEN_1=5meta-externalagent❌ 403
/catalog/category/filter/price-from-1000/meta-externalagent❌ 403
/catalog/category/?sort=pricemeta-externalagent❌ 403

Результаты

После включения блокировки:

МетрикаДоПосле
Утилизация CPU~60%10-15%
Фоновая нагрузкаПостояннаяМинимальная
СтабильностьРебутыСтабильно
Обход основных страницРаботаетРаботает

Дополнительно: блокировка других ботов

Аналогично можно настроить блокировку для других агрессивных ботов:

nginx
map $http_user_agent $is_aggressive_bot {
    default 0;
    ~*meta-externalagent 1;
    ~*AhrefsBot 1;
    ~*SemrushBot 1;
    ~*MJ12bot 1;
    ~*DotBot 1;
}

map "$is_aggressive_bot:$is_heavy_catalog_url" $block_aggressive_bot_heavy {
    default 0;
    "1:1" 1;
}

Мониторинг

Логирование заблокированных запросов стоит настроить сразу: без него вы не узнаете, отсекается ли лишнее и не попал ли под раздачу кто-то нужный.

nginx
log_format blocked '$remote_addr - $http_user_agent - $request_uri';

server {
    # ...

    # Отдельный лог только для заблокированных запросов
    access_log /var/log/nginx/blocked_crawlers.log blocked if=$block_meta_heavy_catalog;

    if ($block_meta_heavy_catalog) {
        return 403;
    }
}
⚠️ Важно

access_log нельзя поместить внутрь if на уровне server. Директива допустима в http, server, location и if in location — при попытке объявить её в блоке if уровня сервера nginx не стартует с ошибкой «directive is not allowed here». Правильный способ условного логирования — параметр if= у самой директивы: значение переменной, отличное от 0 и пустой строки, включает запись.

Анализ логов:

bash
# Топ заблокированных User-Agent
awk '{print $3}' /var/log/nginx/blocked_crawlers.log | sort | uniq -c | sort -nr | head -10

# Топ заблокированных URL
awk '{print $4}' /var/log/nginx/blocked_crawlers.log | sort | uniq -c | sort -nr | head -20

Ограничения этого подхода

Метод рабочий, но у него есть чёткая граница применимости, и о ней лучше знать заранее.

Блокировка по User-Agent работает только с теми, кто честно представляется. Facebook, Ahrefs, Semrush указывают себя в заголовке, потому что заинтересованы в том, чтобы их пускали. Парсер конкурента или скрапер для перепродажи данных подставит Mozilla/5.0 и пройдёт мимо всех правил выше.

Против таких нужен другой инструмент — ограничение частоты, которое работает вне зависимости от того, кем представляется клиент:

nginx
# Отдельная зона для тяжёлых URL каталога
limit_req_zone $binary_remote_addr zone=catalog_heavy:10m rate=2r/s;

location /catalog/ {
    limit_req zone=catalog_heavy burst=10 nodelay;
    # ...
}

return 444 дешевле, чем return 403. Специальный код nginx закрывает соединение без ответа: не тратится время на формирование страницы ошибки и не отдаётся трафик. Для явно нежелательных запросов это разумнее, но применять его к крупным поисковым роботам не стоит — лучше отдать им понятный статус.

403 — сигнал «доступ запрещён навсегда». Если бот вернётся к этим URL, он будет получать тот же ответ и в конце концов уберёт их из своей очереди — что нам и нужно. Но если вы ограничиваете временную нагрузку и хотите, чтобы бот вернулся позже, честнее отдавать 429 с заголовком Retry-After.

Вывод

Если на Битриксе неожиданно растёт CPU без видимого роста продаж:

  1. Смотрите в access-логи nginx, а не только в профилировщик PHP. Профилировщик отвечает на вопрос «что тормозит», логи — на вопрос «кто это заказал», а во внешней нагрузке важен именно второй.
  2. Блокируйте на входе, а не в приложении: цена отброшенного запроса отличается на порядки.
  3. Блокируйте точечно. Полный запрет для крупного краулера — это потеря превью ссылок в соцсетях или пропажа сайта из чьего-то индекса. Отсекать нужно конкретные тяжёлые сценарии, а не агента целиком.
  4. Настройте лимит частоты как второй рубеж — он работает и против тех, кто не представляется.
💡 Совет

Следующий шаг: После устранения внешней нагрузки займитесь внутренней проблемой — почему запросы к каталогу порождали неконтролируемый рост кэша catalog.section.

🚀

Хотите такое же решение?

Настрою окружение под ваш проект, учту специфику инфраструктуры и обучу команду.

Обсудить проект →
Бесплатная консультация · Ответ в течение дня

Комментарии

Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.