SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor

Как исправить ошибку 429 в SearXNG

Ошибка 429 в SearXNG возникает из-за локального лимитера или блокировки IP поисковиком. Изучите лог, чтобы понять причину, и настройте параметры в settings.yml для устранения проблемы.

Почему SearXNG возвращает ошибки 429

Самостоятельно развернутый экземпляр SearXNG возвращает ошибки 429 по двум не связанным между собой причинам, и ограничение частоты запросов (rate limit), которое нужно исправить, обычно не то, о котором вы думаете. Первая причина — локальная: собственный ограничитель SearXNG решил, что запрос поступил от бота, и ответил Too Many Requests статусом 429. Вторая причина — внешняя: поисковая система заблокировала IP-адрес вашего сервера, что доходит до пользователей в виде страницы результатов с отсутствующими данными, а не в виде ошибки 429.

Для этих двух случаев нет общего решения. Ограничитель — ваш, поэтому вы можете его изменить. Блокировка на стороне поисковой системы происходит на стороне Google, поэтому никакие изменения в settings.yml её не снимут. Лог позволяет определить причину примерно за минуту, поэтому начните с него.

Это руководство предполагает использование установки в контейнере, описанной в самостоятельно развернутом экземпляре SearXNG на вашем VPS. Все названия настроек ниже взяты из актуальной документации и исходного кода проекта, проверенных в августе 2026 года.

Изучите лог перед изменением настроек

Воспроизведите проблему, оставив окно с логами открытым.

cd ./searxng/
docker compose logs -f searxng-core

Сообщения ограничителя поступают от логгера с именем searx.limiter и содержат IP-адрес. Срабатывание блокирующего списка записывается как BLOCK 203.0.113.10: matched BLOCKLIST, а срабатывание разрешающего списка — как PASS 203.0.113.10: matched PASSLIST. Если ограничитель не может получить доступ к хранилищу счетчиков, в логе появляется сообщение The limiter requires Valkey, please consult the documentation, что означает, что подсчет запросов полностью прекращен.

Каждая отдельная проверка бота записывается на уровне debug, поэтому по умолчанию вы её не увидите. Включите режим отладки для одного теста в settings.yml:

general:
  debug: true

После этого в лог будут добавляться строки вида NOT OK (http_accept_language) рядом с сетью клиента, указывающие на проверку, которая завершилась неудачей. Не забудьте выключить этот режим после завершения, так как разработчики не рекомендуют использовать рабочие экземпляры с включенной отладкой.

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

HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)

Для этого также предусмотрена отдельная страница. Если параметр enable_metrics оставлен со значением по умолчанию true, ваш экземпляр записывает ошибки движка в /stats/errors, а /preferences показывает, какие движки отвечают в данный момент. Если /stats/errors переполнен, а в логе отсутствуют строки searx.limiter, значит, проблема не в ограничителе.

Зафиксируйте версию перед началом отладки

Настройка контейнера от разработчика состоит из двух файлов.

mkdir -p ./searxng/core-config/
cd ./searxng/

curl -fsSL \
    -O https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
    -O https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example

cp -i .env.example .env

Файл compose загружает docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. Отсутствие значения переменной означает latest, а latest приводит к тому, что версия экземпляра меняется при следующем docker compose pull. В результате настройки, работавшие на прошлой неделе, могут перестать соответствовать коду, который их считывает. Теги SearXNG содержат дату и хеш коммита. Пример тега в исходном .env.example по состоянию на август 2026 года — 2026.3.25-541c6c3cb, поэтому укажите конкретный тег в .env:

SEARXNG_VERSION=2026.3.25-541c6c3cb

Проверьте опубликованные теги, зафиксируйте версию, которую вы протестировали, и проводите отладку на стабильной цели. В том же файле .env хранится ваш секретный ключ, поэтому ознакомьтесь с принципами работы env-файлов и секретов в Docker Compose перед тем, как отправлять этот каталог в репозиторий.

Для работы ограничителя требуется Valkey, иначе он не запустится

Ограничитель подсчитывает количество запросов на каждого клиента, и эти данные должны быть доступны всем рабочим процессам. В качестве хранилища используется Valkey — поддерживаемый форк Redis. В старых руководствах по SearXNG этот параметр называется redis:. В текущих релизах используется valkey:, поэтому копируйте имя ключа из актуальной документации, а не из устаревших публикаций.

use_default_settings: true
server:
  secret_key: "change-this-value"
  limiter: true
  public_instance: false
valkey:
  url: valkey://searxng-valkey:6379/0

В исходном файле compose уже запущен сервис searxng-valkey на базе образа docker.io/valkey/valkey:9-alpine, поэтому данное имя хоста успешно разрешается внутри сети compose. То же самое значение можно задать через переменную окружения SEARXNG_VALKEY_URL, а при размещении SearXNG и Valkey на одном хосте можно использовать URL Unix-сокета (unix:///path/to/socket.sock?db=0).

Последствия отсутствия хранилища зависят от еще одного ключа. При public_instance: false ограничитель записывает ошибку Valkey в лог и отключается, поэтому экземпляр продолжает работу без ограничения частоты запросов. При public_instance: true процесс вызывает sys.exit(1), так как открытый экземпляр с неработающей защитой от ботов за сутки соберет CAPTCHA (полностью автоматизированный публичный тест Тьюринга для различения компьютеров и людей) от всех поисковых движков. Если контейнер уходит в бесконечный цикл перезагрузки сразу после установки public_instance: true, причина именно в этом, а последняя строка перед каждым завершением процесса указывает на Valkey.

Что именно учитывает ограничитель

ChartSearXNG limiter: requests allowed per client IP, defaults in ip_limit.py
The data behind this chart
[
  {
    "label": "Burst, normal client",
    "max_requests": 15,
    "window": "20 seconds"
  },
  {
    "label": "Burst, flagged client",
    "max_requests": 2,
    "window": "20 seconds"
  },
  {
    "label": "Sustained, normal client",
    "max_requests": 150,
    "window": "10 minutes"
  },
  {
    "label": "Sustained, flagged client",
    "max_requests": 10,
    "window": "10 minutes"
  },
  {
    "label": "Any non-HTML format",
    "max_requests": 4,
    "window": "1 hour"
  },
  {
    "label": "Flagged requests before block",
    "max_requests": 3,
    "window": "30 days"
  }
]

Обычный клиент может выполнить 15 запросов в течение 20-секундного окна и 150 в течение 10-минутного окна. Как только запрос помечается как подозрительный, лимит для этого клиента снижается до 2 на окно всплеска. Последняя строка — самая строгая: после 3 подозрительных запросов в течение 30-дневного окна этот адрес перенаправляется на стартовую страницу вместо выполнения поиска, а в лог записывается BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).

Эти значения являются константами в searx/botdetection/ip_limit.py. Они не являются настройками, и limiter.toml не позволяет их изменять, поэтому для их корректировки необходимо редактировать исходный код. /etc/searxng/limiter.toml позволяет управлять префиксами адресов, используемыми для группировки клиентов, списком доверенных прокси, опциональной проверкой токена ссылки, а также списками разрешенных и заблокированных адресов.

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

  • http_accept: заголовок Accept не содержит text/html.
  • http_accept_encoding: заголовок Accept-Encoding не содержит ни gzip, ни deflate.
  • http_accept_language: отсутствует заголовок Accept-Language.
  • http_connection: заголовок Connection установлен в close.
  • http_user_agent: User-Agent отсутствует или соответствует известному шаблону бота.
  • http_sec_fetch: заголовок Sec-Fetch-Mode или Sec-Fetch-Dest не соответствует тому, что отправляет браузер.

Браузер отправляет все эти заголовки. Обычный вызов curl не отправляет почти ничего из этого, поэтому написанный вручную тестовый запрос помечается при первой же попытке, в то время как тот же поиск работает во вкладке браузера. Именно поэтому ситуация «в браузере работает, а мой скрипт получает 429» является закономерным результатом, а не загадкой.

За обратным прокси-сервером ограничитель блокирует всех пользователей сразу

Это самая частая причина сбоя в работе экземпляра. SearXNG получает адрес клиента из первого недоверенного IP в X-Forwarded-For, затем переходит к X-Real-IP, а в конечном итоге использует адрес, с которого было установлено соединение. Решение о том, доверять ли этим заголовкам, определяется параметром trusted_proxies в файле limiter.toml.

Если адрес вашего прокси-сервера отсутствует в этом списке, заголовки игнорируются, и все посетители отображаются с адресом прокси-сервера. В результате они используют один общий счетчик, и весь сайт блокируется, как только суммарное количество запросов превышает 150 за 10 минут. Один пользователь, несколько раз обновивший страницу с результатами, приводит к блокировке всех остальных.

Чрезмерное доверие также опасно. Если в списке указан публичный диапазон, любой посетитель может отправить собственный заголовок X-Forwarded-For и выбирать новую «личность» для каждого запроса, что фактически отключает ограничитель для тех, кто знает об этой возможности. Указывайте только тот адрес, с которого подключается ваш прокси-сервер. В Docker это обычно мостовая сеть внутри 172.16.0.0/12, и эта строка поставляется закомментированной.

[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48

trusted_proxies = [
  '127.0.0.0/8',
  '::1',
  '172.16.0.0/12',
]

Прокси-сервер также должен передавать эти заголовки. Nginx не добавляет их автоматически:

location / {
    proxy_pass http://127.0.0.1:8080;

    proxy_set_header Host              $host;
    proxy_set_header Connection        $http_connection;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
}

Caddy и Traefik устанавливают заголовки пересылки автоматически, поэтому для них достаточно выполнить только часть настройки, связанную с trusted_proxies. Компромиссы при выборе решения описаны в выборе обратного прокси-сервера для self-hosted сервиса. Чтобы проверить корректность настройки, включите debug, выполните один поиск с телефона через мобильную сеть и убедитесь, что в строке лога указан адрес вашего телефона, а не адрес прокси-сервера.

Ваш агент выполняет четыре API-запроса в час

Вывод в формате JSON по умолчанию отключен, поэтому для агента его необходимо добавить:

search:
  formats:
    - html
    - json

Теперь снова изучите строку таблицы. Любой запрос, запрашивающий формат, отличный от HTML, учитывается в рамках собственного окна: 4 запросов за 1 hour на один адрес. Исследовательский агент расходует этот лимит за одну задачу, и каждый последующий вызов возвращает ошибку 429. Увеличение лимита не является решением, так как это число жестко задано в исходном коде.

Правильный способ исправления — сообщить ограничителю (limiter), что этот клиент не является сторонним. Добавьте его адрес в список разрешенных (pass list) в limiter.toml:

[botdetection.ip_lists]
block_ip = []

pass_ip = [
  '10.8.0.0/24',
]

pass_searxng_org = true

pass_ip имеет приоритет над любым другим методом, поэтому клиент из списка разрешенных также пропускает проверки заголовков, и обычный вызов curl работает корректно. Старайтесь указывать как можно более узкий диапазон адресов; отдавайте предпочтение подсети VPN или сети контейнеров, а не маршрутизируемым адресам. Другой правильный способ — полностью исключить агента из публичного пути: направьте его на адрес контейнера во внутренней сети, где прокси-сервер и его ограничитель не видят трафик. Настройка этого процесса описана в предоставлении AI-агенту навыка поиска SearXNG.

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

Когда поисковые движки блокируют запросы

ChartHow long SearXNG suspends an engine, search.suspended_times defaults
The data behind this chart
[
  {
    "label": "SearxEngineTooManyRequests",
    "suspended_seconds": 3600,
    "roughly": "1 hour"
  },
  {
    "label": "SearxEngineAccessDenied",
    "suspended_seconds": 86400,
    "roughly": "1 day"
  },
  {
    "label": "SearxEngineCaptcha",
    "suspended_seconds": 86400,
    "roughly": "1 day"
  },
  {
    "label": "recaptcha_SearxEngineCaptcha",
    "suspended_seconds": 604800,
    "roughly": "7 days"
  },
  {
    "label": "cf_SearxEngineCaptcha",
    "suspended_seconds": 1296000,
    "roughly": "15 days"
  }
]

Если поисковый движок отвечает кодом 429 или страницей с CAPTCHA, SearXNG генерирует именованное исключение и на некоторое время прекращает отправку запросов к этому движку. Ответ с кодом «слишком много запросов» приостанавливает работу движка на 3600 секунд. Обычная CAPTCHA или отказ в доступе приостанавливают его на 1 day. CAPTCHA от Cloudflare приводит к приостановке на 15 days — это самый длительный период ожидания по умолчанию, так как такой ответ означает блокировку на пограничном уровне, и повторные попытки не помогут.

Обычные сбои используют другие настройки. Тайм-аут или ошибка парсинга приостанавливают движок на короткий срок, рассчитываемый на основе search.ban_time_on_fail (по умолчанию 5 секунд, ограничено параметром search.max_ban_time_on_fail до 120 секунд). Таким образом, медленный движок восстанавливается самостоятельно в течение пары минут, тогда как заблокированный движок выпадает из работы на часы. Эта разница объясняет симптом, который пользователи называют случайным: результаты поиска отображаются корректно, а затем результаты одного из движков исчезают до конца дня.

Прежде чем искать виноватых, стоит устранить тайм-ауты. Значение request_timeout по умолчанию составляет 2.0 секунды, что является слишком жестким ограничением для небольшого VPS, расположенного далеко от ближайшего пограничного сервера движка.

outgoing:
  request_timeout: 3.0
  max_request_timeout: 10.0
engines:
  - name: bing
    timeout: 5.0

request_timeout — это значение по умолчанию для каждого движка, max_request_timeout — верхний предел, а отдельный движок может иметь собственный параметр timeout. Увеличение этих значений повышает задержку страницы ради уменьшения количества сбоев, поэтому изменяйте их с шагом в полсекунды и следите за /stats/errors, вместо того чтобы сразу устанавливать 10.

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

use_default_settings:
  engines:
    remove:
      - google

Примените изменения с помощью docker compose restart searxng-core, затем выполните несколько поисковых запросов и перезагрузите /stats/errors. Пустая страница после пяти минут реального использования означает, что изменения сработали.

IP-адрес дата-центра будет распознан как бот

Ваш адрес VPS относится к диапазону хостинг-провайдера, и крупные поисковые системы помечают такие диапазоны как автоматизированные. Некоторые из них выдают CAPTCHA на каждый запрос с такого адреса, независимо от корректности заголовков или низкой частоты запросов. Никакие настройки в settings.yml не изменят это решение.

Вы можете изменить лишь то, к каким поисковым системам обращаться, и сделать ли ваш экземпляр публично доступным. Частный экземпляр, используемый одним домохозяйством, редко вызывает срабатывание защиты. Публичный экземпляр на IP-адресе хостинга будет постоянно получать блокировки от наиболее строгих систем, и это нормальное поведение программного обеспечения, а не ошибка в вашей конфигурации. SearXNG может направлять запросы к поисковым системам через прокси с помощью outgoing.proxies или outgoing.using_tor_proxy, что перенаправляет трафик на другой адрес. Выходные узлы (exit nodes) и дешевые прокси-пулы имеют еще более низкий рейтинг, чем диапазоны хостинг-провайдеров, поэтому будьте готовы к тому, что это ухудшит качество результатов поиска.

Мониторинг экземпляра для оперативного оповещения

SearXNG отвечает на своем порту, даже если все поисковые движки приостановлены. В результате проверка доступности, отслеживающая только код ответа, будет показывать статус «в сети», хотя экземпляр не выдает результатов. Проверяйте содержимое ответа: выполните реальный поисковый запрос и ищите ожидаемое слово в теле ответа. Мониторинг ключевых слов в Uptime Kuma выполняет эту задачу без дополнительных инструментов. Также следите за /stats/errors после каждого обновления версии, так как структура HTML в движках меняется, что приводит к поломке парсера независимо от ограничений по частоте запросов.

FAQ

Почему SearXNG возвращает 429 каждому посетителю после того, как я поместил его за reverse proxy?

Потому что ограничитель считает прокси-сервер клиентом. SearXNG считывает X-Forwarded-For только в том случае, если адрес подключения указан в trusted_proxies в файле /etc/searxng/limiter.toml. Если адрес не указан, все посетители используют один общий счетчик, и они все вместе превышают лимит в 150 запросов за 10 минут. Добавьте адрес, с которого подключается ваш прокси (в Docker это обычно диапазон моста 172.16.0.0/12), и убедитесь, что прокси передает заголовки X-Real-IP и X-Forwarded-For. Никогда не указывайте диапазон, который вы не контролируете, так как доверенная сеть позволяет любому посетителю установить этот заголовок и выбирать новую личность для каждого запроса.

Сколько API-запросов в час разрешает ограничитель SearXNG?

Четыре запроса на один IP-адрес в час. Любой запрос, запрашивающий формат, отличный от HTML, учитывается в отдельном часовом окне, и этот лимит задан в searx/botdetection/ip_limit.py, а не в limiter.toml, поэтому его нельзя увеличить через конфигурацию. Агент или скрипт выполняет это за одну задачу. Добавьте адрес клиента в pass_ip в файле limiter.toml или обращайтесь к экземпляру через внутреннюю сеть, где ограничитель не видит запрос.

Почему мои результаты поиска приходят пустыми без ошибки 429?

Поисковые движки блокируют ваш сервер, а не ваших пользователей. Откройте /stats/errors на вашем экземпляре: там указан каждый движок, который выдал ошибку, и причина. Запись о CAPTCHA или отказе в доступе означает, что движок заблокировал IP-адрес вашего сервера. После этого SearXNG приостанавливает работу движка: на час после ответа о слишком большом количестве запросов и на день после появления CAPTCHA. Никакие локальные настройки не снимут блокировку на стороне провайдера, поэтому удалите движки, которые блокируют ваш адрес, и оставьте те, которые отвечают.

Стоит ли включать ограничитель на приватном экземпляре?

Если к экземпляру никто, кроме вас, не обращается, оставьте limiter: false. Это добавляет зависимость от Valkey, блокирует ваши собственные скрипты и защищает от трафика, которого у вас нет. Включайте его сразу, как только экземпляр получает публичный адрес, вместе с public_instance: true. Эта пара работает согласованно: при public_instance: true и неработающем Valkey процесс завершается со статусом 1, вместо того чтобы работать без защиты.