MAATRIX / Блог / Робот индексатор положил сайт: разбор и защита

Робот индексатор положил сайт: разбор и защита

MAATRIX

Сайт еле открывается или падает, первая мысль — атака. Но в логах ни хаоса, ни сотен адресов: один user-agent, методичный обход страница за страницей, и он честно представился в заголовке запроса. Это не DDoS, а агрессивный поисковый робот или другой автоматический краулер, который слишком интенсивно и параллельно обходит сайт — и особенно достаётся тяжёлым для генерации страницам вроде поиска и фильтров. Ниже — как это распознать, найти виновника в логах и настроить сервер так, чтобы легитимный обход не укладывал сайт на лопатки.

Это не DDoS: как отличить агрессивного робота от атаки

Первый шаг — не бросаться в панику и не банить половину подсетей, а понять, с чем вы имеете дело. У агрессивного краулера и у DDoS-атаки принципиально разный почерк, и спутать их — значит либо зря воевать с несуществующим врагом, либо упустить настоящую проблему.

Признаки легитимного, но избыточного робота:

  • Один явный источник. Весь всплеск трафика идёт с одного user-agent (YandexBot, Googlebot, AhrefsBot, SemrushBot, MJ12bot, PetalBot, или UA конкурентского парсера цен) или с чёткого диапазона IP известной компании.
  • Методичный, а не хаотичный паттерн. Запросы идут последовательно по страницам каталога, по алфавиту, по возрастающим ID или по перебору параметров фильтра — видна логика, а не случайный шум.
  • Уважает (или может уважать) robots.txt. Настоящие поисковые роботы и большинство серьёзных SEO-инструментов проверяют robots.txt перед обходом. Поставьте правильные Disallow — и трафик от них снизится за сутки-двое. DDoS на правила в robots.txt не реагирует никак.
  • Заголовки выглядят «по-браузерному» либо честно бот-подобно. У легитимных краулеров обычно корректный Accept-Encoding, разумные интервалы между запросами и часто указан URL с описанием бота (+http://yandex.com/bots).

Признаки настоящей атаки — наоборот: десятки-сотни разных источников без общей логики, случайные или поддельные user-agent, запросы к несвязанным URL без всякой системы, полное игнорирование robots.txt, аномальные заголовки. Если картина именно такая — это тема отдельного разбора: что делать в первые минуты при DDoS-атаке описано отдельно, и меры там другие — фильтрация на уровне провайдера, а не тонкая настройка робота.

Не путайте эти два случая. Правила против DDoS (агрессивный iptables, блокировка по гео, экстренное включение защиты у провайдера), применённые к легитимному краулеру, в лучшем случае бесполезны, в худшем — вы наглухо баните Яндекс или Google и теряете позиции в поиске на недели. А меры против робота (тонкая настройка robots.txt и rate limiting по user-agent) против настоящей атаки не сработают вообще — там нет ни robots.txt, который кто-то будет читать, ни постоянного user-agent, по которому можно резать.

Что показывает access.log: ищем источник

Диагностика начинается с лога nginx или apache — там всегда виден источник конкретной нагрузки. Начните с общей картины: кто вообще создаёт основной объём запросов.

# Топ user-agent по числу запросов за текущий лог
awk -F\" '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Если наверху списка не «Mozilla/5.0 (обычный браузер)», а что-то вроде Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots) или Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/) с многократным перевесом над остальными — вот и первый след. Дальше смотрите, какие именно URL этот бот дёргает:

grep "YandexBot" /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -30

Практически всегда картина одна и та же: сотни и тысячи запросов на страницы вида /catalog/noutbuki/?sort=price_desc&brand=asus&ram=16 — то есть на страницы поиска или фильтров с параметрами, а не на статичные карточки товаров. Это и есть суть проблемы: робот честно и методично обходит каждую комбинацию фильтра, которую в принципе можно построить по ссылкам на сайте.

Отдельно стоит проверить, действительно ли это тот, за кого себя выдаёт user-agent — иногда парсеры конкурентов маскируются под Googlebot, чтобы их не банили. Настоящих Googlebot и YandexBot можно проверить через обратный DNS:

# IP из подозрительных строк лога
host 5.255.253.100
# Ответ вида spider.yandex.com — это настоящий YandexBot
# Если ответа нет или домен левый — это подделка под user-agent

Если DNS не подтверждает принадлежность к заявленной компании — перед вами маскирующийся парсер, и тут уже можно применять более жёсткие меры без риска забанить настоящий поисковик. Общий подход к чтению логов и поиску причины сбоя — в статье как читать логи и находить причину сбоя, здесь же специфика именно краулерного паттерна — концентрация на конкретных параметризованных URL от одного источника, а не размазанная по всему сайту нагрузка.

Также полезно посмотреть время ответа именно на эти URL — если $request_time показывает 2-5 секунд на страницы фильтров против 0,05-0,1 секунды на статичные страницы, это подтверждает: узкое место не в объёме трафика, а в цене генерации конкретных страниц.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Почему страницы фильтров и поиска — самое уязвимое место

Ключевая причина, почему именно этот тип трафика способен положить сайт при скромном числе запросов в секунду — комбинаторность. У каталога с фильтрами по бренду, цене, характеристикам и сортировке число возможных URL растёт перемножением вариантов каждого параметра: десять брендов × пять диапазонов цены × четыре типа сортировки — уже сотни уникальных для сервера URL одной и той же логической страницы каталога.

Для человека это один и тот же раздел сайта, который он открывает один раз, а дальше кликает по фильтрам через AJAX. Для робота, который честно обходит все ссылки-комбинации, это тысячи разных страниц — и для сервера это тоже тысячи разных страниц, потому что URL разный: кеш по URL на них не срабатывает, и почти всегда приходится каждый раз заново идти в базу, применять фильтр, сортировку, лимиты, собирать выдачу и рендерить шаблон.

Карточка товара или главная страница обычно кешируется целиком и отдаётся за миллисекунды даже под нагрузкой. А результат ?sort=price_desc&brand=asus&ram=16&color=black почти никогда не лежит в кеше заранее — слишком много возможных комбинаций, чтобы прогревать их все. Каждый такой запрос — это полноценный поход в СУБД с JOIN и сортировкой, и если таких запросов параллельно прилетает пятьдесят в секунду от одного добросовестного, но быстрого краулера — это создаёт нагрузку, сравнимую по эффекту с DDoS, хотя суммарный RPS может быть довольно скромным по меркам защиты от атак.

Усугубляет ситуацию то, что многие роботы (особенно SEO-инструменты вроде Screaming Frog, запущенные конкурентом, или парсеры цен) идут в несколько параллельных потоков, не дожидаясь ответа на предыдущий запрос. Если у backend ограниченное число воркеров (скажем, PHP-FPM с pm.max_children = 10), достаточно 10-15 одновременных «тяжёлых» запросов от одного робота, чтобы все воркеры оказались заняты медленными SQL-запросами, а обычные посетители начали получать таймауты — сайт «лёг» без единого признака атаки в классическом смысле.

Robots.txt: ограничиваем то, что не нужно индексировать

Первая и самая дешёвая мера — правильный robots.txt. Важно понимать: он не остановит поддельного бота или парсера, который его не читает, но реально снизит нагрузку от легитимных поисковых роботов, которым нет смысла тратить краулинговый бюджет на бесконечные комбинации фильтров — эти страницы всё равно не нужны в индексе.

User-agent: *
Disallow: /*?*sort=
Disallow: /*?*filter=
Disallow: /*?*page=
Disallow: /search?
Allow: /catalog/$
Allow: /catalog/*/$

User-agent: Yandex
Disallow: /*?*sort=
Disallow: /*?*filter=
Clean-param: sort&filter&page&utm_source&utm_medium /catalog/
Crawl-delay: 2

Директива Clean-param понимает только Яндекс — она говорит роботу, что параметры sort, filter, page и utm-метки не меняют содержимое страницы по сути и их можно не учитывать при обходе, схлопывая тысячи URL в один. Crawl-delay тоже читает только Яндекс (Google эту директиву игнорирует — скорость его обхода регулируется через Search Console или ответы сервера кодом 429/503), но она полезна, если основную нагрузку создаёт именно YandexBot.

Важная оговорка: Disallow в robots.txt — рекомендация для добросовестных роботов, а не техническая защита. Если конкурентский парсер игнорирует robots.txt (а многие коммерческие парсеры цен так и делают), нужен уже rate limiting на уровне сервера — следующий раздел. Проверить, что правила реально работают, можно спустя 2-3 дня через тот же лог: доля запросов от YandexBot/Googlebot на URL с ?sort= и ?filter= должна заметно упасть.

Rate limiting только для ботов, не для людей

Если robots.txt не решает проблему полностью, следующий шаг — ограничить частоту запросов именно для ботов, не трогая обычных посетителей. В nginx это делается через map по user-agent и отдельную зону limit_req:

http {
    # Общая зона для всех — щедрый лимит, чтобы не мешать людям
    limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;

    # Зона только для распознанных ботов — гораздо жёстче
    map $http_user_agent $bot_ua {
        default          "";
        ~*YandexBot      "bot";
        ~*Googlebot      "bot";
        ~*bingbot        "bot";
        ~*AhrefsBot      "bot";
        ~*SemrushBot     "bot";
        ~*MJ12bot        "bot";
        ~*DotBot         "bot";
        ~*PetalBot       "bot";
    }
    limit_req_zone $binary_remote_addr zone=bots:10m rate=2r/s;

    server {
        location / {
            limit_req zone=general burst=50 nodelay;
        }

        # Тяжёлые для генерации разделы — отдельный, жёсткий лимит для ботов
        location /catalog/ {
            limit_req zone=general burst=50 nodelay;
            if ($bot_ua = "bot") {
                limit_req zone=bots burst=4 nodelay;
            }
            try_files $uri @backend;
        }
    }
}

Для обычных посетителей (bot_ua пустой) работает только щедрая зона general — они этого лимита практически никогда не почувствуют. А для распознанных ботов на тяжёлых URL применяется вторая, куда более жёсткая зона bots — 2 запроса в секунду с всплеском до 4. Это снижает нагрузку от честного, но избыточного робота в разы, не блокируя его полностью (полная блокировка Googlebot или YandexBot по IP — плохая идея, это бьёт по видимости сайта в поиске).

Для источников, которые явно не поисковики, а парсеры конкурентов без пользы для SEO (список выше стоит пересмотреть под свою ситуацию — у AhrefsBot и SemrushBot, например, есть легитимное применение, если вы сами их используете), лимит можно сделать ещё жёстче или вовсе вернуть 429 через limit_req_status 429;. Общий подход к настройке правил для конкретных источников нагрузки на nginx описан в статье про настройку fail2ban для nginx — тот же принцип «различать по паттерну, а не банить всех» применим и здесь.

Кеширование и canonical: убираем бесконечные дубли

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

Кеширование результатов на уровне nginx (через fastcgi_cache или proxy_cache, в зависимости от бэкенда) значительно снижает цену повторных запросов к одной и той же комбинации параметров:

fastcgi_cache_path /var/cache/nginx/catalog levels=1:2 keys_zone=CATALOG:100m inactive=30m max_size=1g;

server {
    location /catalog/ {
        fastcgi_cache CATALOG;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_cache_valid 200 10m;
        fastcgi_cache_valid 404 1m;
        fastcgi_cache_use_stale error timeout updating http_500 http_502 http_503;
        fastcgi_cache_lock on;
        fastcgi_cache_lock_timeout 5s;
        add_header X-Cache-Status $upstream_cache_status;
        fastcgi_pass backend;
    }
}

Ключевые детали здесь — fastcgi_cache_lock, который не даёт нескольким параллельным запросам к одной и той же редкой комбинации фильтра одновременно долбить бэкенд (первый запрос идёт в базу, остальные ждут готовый результат из кеша), и fastcgi_cache_use_stale, который отдаёт чуть устаревший, но валидный ответ, если бэкенд в этот момент перегружен. Даже 10 минут кеша на страницы фильтров снимают львиную долю нагрузки от повторного обхода популярных комбинаций (sort=price_asc без фильтров запрашивают на порядок чаще редких сочетаний пяти параметров сразу). Подробный разбор кеширования в nginx с частыми граблями — в статье про настройку кеширования nginx.

Второй инструмент — canonical-теги. Они не снижают нагрузку от самого обхода напрямую, но не дают роботу тратить краулинговый бюджет на бесконечные вариации одной страницы, которые всё равно не должны попасть в индекс раздельно:

<link rel="canonical" href="https://example.ru/catalog/noutbuki/" />

Такой тег на странице /catalog/noutbuki/?sort=price_desc&color=black честно говорит роботу: «настоящая, каноническая версия этой страницы — вот эта, без параметров, индексируй именно её». Со временем это снижает частоту обхода вариаций с параметрами — краулер видит, что они помечены как неканонические, и переключает основное внимание на исходную страницу. Это не мгновенный эффект (обычно недели, а не дни), но в сочетании с Disallow/Clean-param из robots.txt и rate limiting он закрывает проблему системно, а не разовым патчем.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли просто заблокировать IP робота через iptables и забыть?

Можно, но рискованно: диапазоны IP крупных поисковиков периодически меняются, и жёсткая блокировка либо перестанет работать через месяц, либо заденет соседний легитимный диапазон. Полная блокировка Googlebot или YandexBot по IP убирает сайт из индекса — это болезненнее временной просадки производительности. Рекомендуется идти через rate limiting и robots.txt, а не через полный бан.

Стоит ли банить AhrefsBot и SemrushBot полностью?

Зависит от того, используете ли вы сами эти инструменты. Если нет — их обход не даёт вам пользы, и ограничить их сильнее, чем Googlebot/YandexBot, разумно. Полный бан тоже не запрещён — в отличие от настоящих поисковиков, это не повлияет на видимость в поиске.

Проблема вернётся после того, как я один раз настрою robots.txt и лимиты?

Может вернуться в другом виде — появится новый парсер с другим user-agent, или каталог обрастёт новыми комбинациями фильтров. Стоит периодически (раз в квартал) перепроверять топ user-agent в логе, чтобы не пропустить нового агрессивного краулера.

Как понять, что нагрузку создаёт именно робот, а не рост реальных посетителей?

Смотрите на распределение по времени суток: реальный наплыв даёт всплеск в характерные часы активности аудитории и рост уникальных сессий, тогда как обход робота идёт равномерно круглосуточно с одного user-agent без разброса поведения, характерного для живых людей.

Нужно ли писать в поддержку Яндекса или Google о проблеме?

Если это легитимный робот, писать почти бесполезно — скорость обхода регулируется настройками на вашей стороне (Crawl-delay, Clean-param, ответы 429/503) и через Yandex Webmaster / Search Console. Если это подозрение на маскировку под робота (не подтверждается обратным DNS) — вопрос уже к вашей собственной защите, а не к поисковику.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →