Як виправити 429 у SearXNG і ліміти запитів
Помилки SearXNG 429 мають дві причини: власний limiter або блокування IP пошуковою системою. Перевірте журнал і застосуйте правильне виправлення.
Чому SearXNG повертає помилки 429
Self-hosted інстанс SearXNG повертає помилки 429 з двох не пов’язаних між собою причин. Ліміт швидкості, який потрібно змінити, зазвичай не той, про який ви думаєте. Перша причина локальна: власний обмежувач SearXNG визначив, що запит надійшов від бота, і відповів Too Many Requests зі статусом 429. Друга причина пов’язана з upstream: пошукова система відхилила IP-адресу вашого сервера. Для користувачів це проявляється як сторінка результатів із відсутніми даними, а не як помилка 429.
Для цих двох випадків потрібні різні виправлення. Обмежувач належить вам, тому його можна змінити. Блокування upstream відбувається на стороні Google, тому жодна зміна у вашому settings.yml не зніме його. Приблизно за хвилину журнал покаже, який саме випадок у вас виник, тому почніть із нього.
У цьому посібнику передбачається встановлення в контейнері, описане в self-hosted інстанс SearXNG на власному VPS. Усі наведені нижче назви параметрів взято з актуальної документації та вихідного коду upstream і перевірено в August 2026.
Прочитайте журнал, перш ніж змінювати параметр
Відтворіть проблему, коли журнал відкритий для перегляду.
cd ./searxng/
docker compose logs -f searxng-coreПовідомлення limiter надходять від logger з назвою searx.limiter і містять IP-адресу. Влучання в blocklist має вигляд BLOCK 203.0.113.10: matched BLOCKLIST, а в allowlist — PASS 203.0.113.10: matched PASSLIST. Якщо limiter не може отримати доступ до сховища лічильників, у журналі з’являється The limiter requires Valkey, please consult the documentation. Це означає, що підрахунок узагалі не виконується.
Кожна окрема перевірка bot записується на рівні debug, тому за замовчуванням ви її не побачите. Увімкніть debug для одного тесту в settings.yml:
general:
debug: trueПісля цього в журналі поруч із мережею клієнта з’являться рядки формату NOT OK (http_accept_language) із назвою перевірки, яка завершилася помилкою. Після тесту знову вимкніть debug, оскільки upstream не рекомендує запускати розгорнутий екземпляр із увімкненим debug.
Помилки engine мають зовсім інший вигляд. Вони містять назву engine, а не IP-адресу. Найпоширеніша помилка — timeout:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)Для цього також є окрема сторінка. Якщо enable_metrics має стандартне значення true, ваш екземпляр записує помилки engine у /stats/errors, а /preferences показує, які engine наразі відповідають. Якщо /stats/errors заповнений, а в журналі немає рядків searx.limiter, проблема не в limiter.
Зафіксуйте версію, перш ніж щось налагоджувати
Конфігурація контейнера від upstream складається з двох файлів.
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 містять дату та commit. Станом на серпень 2026 року приклад тегу у .env.example від upstream — 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 від upstream уже запускає сервіс searxng-valkey на образі docker.io/valkey/valkey:9-alpine, тому це ім’я хоста доступне в мережі compose. Те саме значення можна задати змінною середовища SEARXNG_VALKEY_URL. URL Unix-сокета (unix:///path/to/socket.sock?db=0) працює, якщо SearXNG і Valkey працюють на одному хості.
Поведінка за відсутності сховища залежить ще від одного ключа. З параметром public_instance: false лімітер записує помилку Valkey і припиняє роботу. У результаті інстанс продовжує обслуговувати запити без будь-якого обмеження частоти. З параметром public_instance: true процес натомість викликає sys.exit(1), оскільки відкритий інстанс зі зламаним захистом від ботів збирає CAPTCHA (повністю автоматизований публічний тест Тюрінга для розрізнення комп’ютерів і людей) від кожного рушія протягом доби. Якщо контейнер починає циклічно перезапускатися одразу після встановлення public_instance: true, причина саме в цьому. Останній рядок перед кожним завершенням процесу містить назву Valkey.
Що насправді підраховує обмежувач
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» є очікуваним результатом, а не загадкою.
За reverse proxy limiter блокує всіх одночасно
Це найпоширеніший спосіб зламати робочий інстанс. SearXNG бере адресу клієнта з першої недовіреної IP-адреси в X-Forwarded-For, якщо її немає — з X-Real-IP, а потім — з адреси, яка відкрила з’єднання. Чи вважаються ці заголовки довіреними, визначає trusted_proxies у limiter.toml.
Якщо адресу вашого proxy не внесено до цього списку, заголовки ігноруються, і кожен відвідувач приходить з адресою proxy. Усі вони використовують один лічильник, тому весь сайт блокується одночасно, щойно загальна кількість перевищує 150 запитів за 10 хвилин. Один користувач, який кілька разів перезавантажить сторінку результатів, заблокує всіх інших.
Надмірна довіра ще небезпечніша. Якщо до списку внесено публічний діапазон, будь-який відвідувач може надіслати власний заголовок X-Forwarded-For і вибирати нову ідентичність для кожного запиту. Це вимикає limiter для кожного, хто знає про цю можливість. Додавайте до списку лише адресу, з якої підключається ваш proxy. У Docker це зазвичай адреса bridge network у 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',
]Proxy також має надсилати ці заголовки. 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 самі налаштовують forwarded headers, тому з ними потрібно виконати лише частину роботи, пов’язану з trusted_proxies. Компроміси розглянуто в розділі як вибрати reverse proxy для self-hosted сервісу. Щоб перевірити будь-яку з конфігурацій, увімкніть debug, один раз виконайте пошук із телефона через мобільну мережу та переконайтеся, що в рядку журналу вказано адресу телефона, а не адресу proxy.
Ваш агент отримує чотири API-запити на годину
Виведення JSON типово вимкнене, тому його потрібно додати для агента:
search:
formats:
- html
- jsonТепер знову перегляньте рядок таблиці. Кожен запит із форматом, відмінним від HTML, враховується у власному вікні: 4 запитів на 1 hour, для кожної адреси. Дослідницький агент витрачає цей ліміт в одному завданні, а кожен наступний виклик повертає 429. Підвищити ліміт неможливо, оскільки це число задане у вихідному коді.
Правильне рішення — повідомити limiter, що цей клієнт не є стороннім. Додайте його адресу до списку дозволених у limiter.toml:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip має пріоритет над усіма іншими методами, тому клієнт зі списку дозволених також пропускає перевірки заголовків, а звичайний виклик curl працює. Діапазон має бути якомога меншим. Замість будь-якої маршрутизованої мережі краще використовувати підмережу VPN або мережу контейнерів. Інше правильне рішення — повністю прибрати агента з публічного шляху: спрямувати його на адресу контейнера у внутрішній мережі, де proxy та його limiter не бачать трафік. Налаштування цього варіанта описано в розділі надання AI-агенту навички пошуку в SearXNG.
Не використовуйте варіант, за якого агент звертається до публічного інстансу, запущеного кимось іншим. Це найшвидший спосіб домогтися блокування IP-адреси волонтера з боку зовнішніх пошукових систем. Саме тому формат JSON типово вимкнено.
Коли пошукові рушії блокують вас
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, розташованого далеко від найближчого edge-сервера рушія, цього може бути недостатньо.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout — типове значення для кожного рушія, max_request_timeout — максимальне значення, а окремий рушій може мати власне значення timeout. Збільшення цих значень зменшує кількість помилок, але збільшує затримку завантаження сторінки. Змінюйте їх кроком у пів секунди та стежте за /stats/errors, замість того щоб одразу встановлювати значення 10.
Якщо рушій справді блокує вашу адресу, вилучіть його. Кожен пошук очікує на найдовше відповідаючий рушій, тому постійно призупинений рушій збільшує затримку й не повертає результатів.
use_default_settings:
engines:
remove:
- googleЗастосуйте зміни за допомогою docker compose restart searxng-core, потім виконайте кілька пошуків і перезавантажте /stats/errors. Якщо після п’яти хвилин реального використання сторінка порожня, зміна спрацювала.
IP-адресу дата-центру буде сприйнято як адресу бота
Адреса вашого VPS належить до діапазону хостинг-провайдера, і великі пошукові системи оцінюють такі діапазони як джерела автоматизованих запитів. Деякі з них показують CAPTCHA для кожного запиту з такої адреси, незалежно від коректності заголовків або низької частоти запитів. Жодне налаштування в settings.yml не змінює цієї оцінки.
Ви можете змінити пошукові системи, до яких надсилаєте запити, а також вирішити, чи буде ваш екземпляр доступний публічно. Приватний екземпляр, яким користується одна родина, рідко викликає блокування. Публічний екземпляр на хостинговій IP-адресі отримуватиме блокування в найсуворіших пошукових системах. Це нормальна поведінка програмного забезпечення, а не помилка конфігурації. SearXNG може надсилати запити до пошукових систем через proxy за допомогою outgoing.proxies або outgoing.using_tor_proxy, перенаправляючи мережевий трафік на іншу адресу. Вихідні вузли та дешеві пули proxy оцінюються гірше, ніж хостингові діапазони, тому після такої зміни результати можуть погіршитися.
Спостерігайте за інстансом, щоб одразу виявляти проблеми
SearXNG відповідає на своєму порту, навіть коли всі рушії призупинені. Тому перевірка доступності, яка відстежує лише код стану, залишається успішною, хоча інстанс не повертає результатів. Перевіряйте вміст: виконуйте реальний пошук і шукайте в тілі відповіді слово, яке має там бути. Моніторинг ключових слів в Uptime Kuma робить саме це без додаткових інструментів. Після кожного оновлення версії також перевіряйте /stats/errors, оскільки рушії змінюють свій HTML, і парсер може перестати працювати без жодного обмеження частоти запитів.
FAQ
Чому SearXNG повертає 429 кожному відвідувачу після розміщення за reverse proxy?
Тому що limiter визначає proxy як клієнта. SearXNG читає лише X-Forwarded-For, якщо адреса підключення зазначена в trusted_proxies у /etc/searxng/limiter.toml. Якщо її не зазначити, усі відвідувачі використовують один лічильник і разом перевищують межу 150 запитів за 10 хвилин. Додайте адресу, з якої підключається proxy. У Docker це зазвичай діапазон bridge 172.16.0.0/12. Також переконайтеся, що proxy передає X-Real-IP і X-Forwarded-For. Ніколи не додавайте діапазон, яким не керуєте, оскільки довірена мережа дає будь-якому відвідувачу змогу встановлювати цей заголовок і вибирати нову ідентичність для кожного запиту.
Скільки API-запитів за годину дозволяє limiter SearXNG?
Чотири на одну IP-адресу за годину. Будь-який запит, який запитує формат, відмінний від HTML, враховується в окремому одногодинному вікні. Цю межу задано в searx/botdetection/ip_limit.py, а не в limiter.toml, тому її не можна збільшити в конфігурації. Агент або скрипт перевищує її в межах одного завдання. Додайте адресу клієнта до pass_ip у limiter.toml або підключайтеся до інстансу через внутрішню мережу, де limiter не бачить запит.
Чому результати пошуку повертаються порожніми без помилки 429?
Зовнішні engines блокують ваш server, а не користувачів. Відкрийте /stats/errors у власному інстансі. Цей файл містить назву кожного engine, який завершився помилкою, і причину. Запис CAPTCHA або access denied означає, що engine заблокував IP-адресу вашого server. Після відповіді too many requests SearXNG призупиняє engine на годину, а після CAPTCHA — на добу. Жоден локальний параметр не скасовує блокування на стороні upstream, тому вилучіть engines, які блокують вашу адресу, і залиште ті, що відповідають.
Чи слід увімкнути limiter у приватному інстансі?
Якщо до інстансу підключаєтеся лише ви, залиште limiter: false. Він додає залежність від Valkey і блокує ваші власні скрипти, але захищає від трафіку, якого у вас немає. Увімкніть його, щойно інстанс отримає публічну адресу, разом із public_instance: true. Ця пара параметрів потрібна навмисно: з public_instance: true і непрацюючим Valkey процес завершується зі статусом 1, а не запускається без захисту.