SearXNG: 429-Fehler und Rate-Limits richtig beheben
SearXNG 429-Fehler haben zwei Ursachen: Ihr eigener Limiter oder eine geblockte Server-IP. Prüfen Sie das Log und beheben Sie gezielt den richtigen Fall.
Warum SearXNG 429-Fehler zurückgibt
Eine selbst gehostete SearXNG-Instanz gibt aus zwei voneinander unabhängigen Gründen 429-Fehler zurück. Das Rate-Limit, das Sie anpassen müssen, ist normalerweise nicht das, was Sie zunächst vermuten. Der erste Grund liegt lokal: Der eigene Limiter von SearXNG stuft eine Anfrage als von einem Bot stammend ein und beantwortet sie Too Many Requests mit dem Status 429. Der zweite Grund liegt beim Upstream: Eine Suchmaschine lehnt die IP-Adresse Ihres Servers ab. Für Ihre Benutzer äußert sich das als Ergebnisseite mit fehlenden Inhalten und nicht als 429.
Für die beiden Fälle gibt es keine gemeinsame Lösung. Der Limiter gehört zu Ihrer Instanz, daher können Sie ihn ändern. Eine Sperre beim Upstream erfolgt auf der Seite von Google. Daher kann keine Einstellung in Ihrer settings.yml die Sperre aufheben. Anhand des Logs erkennen Sie innerhalb von etwa einer Minute, welcher Fall vorliegt. Beginnen Sie daher dort.
Diese Anleitung setzt die in eine selbst gehostete SearXNG-Instanz auf Ihrem eigenen VPS beschriebene Container-Installation voraus. Alle unten genannten Einstellungsnamen stammen aus der aktuellen Upstream-Dokumentation und dem Quellcode, die im August 2026 geprüft wurden.
Lesen Sie das Log, bevor Sie eine Einstellung ändern
Reproduzieren Sie das Problem mit geöffnetem Log-Fenster.
cd ./searxng/
docker compose logs -f searxng-coreLimiter-Meldungen stammen vom Logger searx.limiter und enthalten eine IP-Adresse. Ein Treffer in der Blocklist wird als BLOCK 203.0.113.10: matched BLOCKLIST protokolliert, ein Treffer in der Allowlist als PASS 203.0.113.10: matched PASSLIST. Wenn der Limiter seinen Counter-Speicher nicht erreichen kann, enthält das Log The limiter requires Valkey, please consult the documentation. Dann werden überhaupt keine Zählerwerte erfasst.
Jede einzelne Bot-Prüfung wird auf Debug-Level protokolliert und ist daher standardmäßig nicht sichtbar. Aktivieren Sie Debug für einen Test in settings.yml:
general:
debug: trueDas Log enthält dann neben dem Client-Netzwerk Zeilen im Format NOT OK (http_accept_language). Diese nennen die fehlgeschlagene Prüfung. Deaktivieren Sie Debug anschließend wieder, da der Upstream davon abrät, eine bereitgestellte Instanz mit aktiviertem Debug zu betreiben.
Fehler der Engine sehen ganz anders aus. Sie nennen statt einer IP-Adresse eine Engine. Am häufigsten tritt ein Timeout auf:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)Dafür gibt es ebenfalls eine Seite. Wenn enable_metrics den Standardwert true verwendet, protokolliert Ihre Instanz Engine-Fehler auf /stats/errors. /preferences listet auf, welche Engines derzeit antworten. Wenn /stats/errors voll ist und das Log keine Zeilen mit searx.limiter enthält, liegt das Problem nicht beim Limiter.
Version festlegen, bevor Sie mit der Fehlersuche beginnen
Das Container-Setup des Upstream-Projekts besteht aus zwei Dateien.
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 .envDie Compose-Datei lädt docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. Eine nicht gesetzte Variable bedeutet latest. latest bedeutet, dass sich die Instanz beim nächsten docker compose pull ändert. Eine Einstellung, die letzte Woche funktioniert hat, kann dann nicht mehr zum Code passen, der sie ausliest. SearXNG-Tags enthalten ein Datum und einen Commit. Der Beispiel-Tag im Upstream-.env.example mit Stand August 2026 lautet 2026.3.25-541c6c3cb. Setzen Sie daher in .env einen konkreten Wert:
SEARXNG_VERSION=2026.3.25-541c6c3cbPrüfen Sie die veröffentlichten Tags und legen Sie das Release fest, das Sie tatsächlich getestet haben. Führen Sie die Fehlersuche anschließend mit einem unveränderten Ziel durch. In derselben Datei .env befindet sich auch Ihr geheimer Schlüssel. Lesen Sie daher wie Umgebungsdateien und Secrets in Docker Compose funktionieren, bevor Sie dieses Verzeichnis an einen beliebigen Ort übertragen.
Der Limiter benötigt Valkey, sonst läuft er nicht
Der Limiter zählt Anfragen pro Client. Diese Zähler müssen zwischen den Worker-Prozessen gemeinsam genutzt werden. Dieser Speicher ist Valkey, der gepflegte Fork von Redis. Ältere SearXNG-Anleitungen nennen diese Einstellung redis:. Aktuelle Releases lesen valkey:. Übernehmen Sie den Schlüsselnamen daher aus der aktuellen Dokumentation und nicht aus einem älteren Beitrag. Einige dieser Seiten gehen noch weiter zurück und beschreiben Searx statt SearXNG. Dabei handelt es sich um eine andere Codebasis mit einem anderen Limiter. Prüfen Sie daher, für welches der beiden Projekte eine Seite geschrieben wurde, bevor Sie daraus einen Konfigurationsblock übernehmen.
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0Die Upstream-Compose-Datei startet bereits einen searxng-valkey-Dienst mit dem Image docker.io/valkey/valkey:9-alpine. Dieser Hostname wird daher innerhalb des Compose-Netzwerks aufgelöst. Derselbe Wert kann über die Umgebungsvariable SEARXNG_VALKEY_URL gesetzt werden. Auch eine Unix-Socket-URL (unix:///path/to/socket.sock?db=0) funktioniert, wenn SearXNG und Valkey denselben Host verwenden.
Was passiert, wenn der Speicher fehlt, hängt von einem weiteren Schlüssel ab. Mit public_instance: false protokolliert der Limiter den Valkey-Fehler und gibt auf. Die Instanz verarbeitet dann weiterhin Anfragen, jedoch vollständig ohne Rate-Limiting. Mit public_instance: true ruft der Prozess stattdessen sys.exit(1) auf. Eine offene Instanz mit defektem Bot-Schutz sammelt sonst innerhalb eines Tages CAPTCHAs (completely automated public turing test to tell computers and humans apart) von jeder Suchmaschine. Wenn ein Container direkt nach dem Setzen von public_instance: true in einer Neustartschleife läuft, ist dies die Ursache. Die letzte Zeile vor jedem Beenden nennt Valkey.
Was der Limiter tatsächlich zählt
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"
}
]Ein normaler Client erhält innerhalb eines Burst-Fensters von 20 Sekunden 15 Anfragen und innerhalb eines Fensters von 10 Minuten 150. Sobald eine Anfrage als verdächtig markiert wurde, sinkt das Limit für denselben Client auf 2 pro Burst-Fenster. Die letzte Zeile ist am strengsten: Nach 3 markierten Anfragen innerhalb eines Fensters von 30 Tagen wird diese Adresse an den Anfang der Seite weitergeleitet, statt die Suche auszuführen. Im Log steht dann BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).
Diese Zahlen sind Konstanten in searx/botdetection/ip_limit.py. Sie sind keine Einstellungen, und limiter.toml stellt sie nicht bereit. Eine Änderung erfordert daher eine Bearbeitung des Quellcodes. Mit /etc/searxng/limiter.toml lassen sich dagegen die Adresspräfixe zur Gruppierung von Clients, die Liste vertrauenswürdiger Proxys, die optionale Prüfung von Link-Tokens sowie die Allow- und Blocklisten konfigurieren.
Eine Anfrage wird durch Header-Prüfungen als verdächtig markiert. Jede Prüfung hat einen Namen, der im Debug-Log erscheint:
http_accept: Der HeaderAcceptenthält nichttext/html.http_accept_encoding: Der HeaderAccept-Encodingnennt wedergzipnochdeflate.http_accept_language: Es gibt keinen HeaderAccept-Language.http_connection: Der HeaderConnectionist aufclosegesetzt.http_user_agent:User-Agentfehlt oder entspricht einem bekannten Bot-Muster.http_sec_fetch: Der HeaderSec-Fetch-ModeoderSec-Fetch-Destentspricht nicht dem, was ein Browser sendet.
Ein Browser sendet alle diese Header. Ein einfacher curl-Aufruf sendet fast keinen davon. Deshalb wird eine manuell erstellte Testanfrage bereits beim ersten Versuch markiert, während dieselbe Suche in einem Browser-Tab funktioniert. Daher ist „In meinem Browser funktioniert es, aber mein Script erhält 429“ das normale Ergebnis und kein rätselhaftes Verhalten.
Der Limiter blockiert hinter einem Reverse Proxy alle gleichzeitig
Das ist die häufigste Ursache dafür, dass eine funktionierende Instanz ausfällt. SearXNG übernimmt die Client-Adresse aus der ersten nicht vertrauenswürdigen IP-Adresse in X-Forwarded-For, verwendet ersatzweise X-Real-IP und greift danach auf die Adresse zurück, von der die Verbindung geöffnet wurde. Ob diese Header überhaupt berücksichtigt werden, wird in trusted_proxies innerhalb von limiter.toml festgelegt.
Wenn die Adresse Ihres Proxys nicht in dieser Liste enthalten ist, werden die Header ignoriert. Jeder Besucher erscheint dann mit der Adresse des Proxys. Alle teilen sich dadurch einen Zähler. Sobald insgesamt mehr als 150 Anfragen in 10 Minuten eingehen, wird die gesamte Website gemeinsam blockiert. Ein Benutzer muss nur einige Male eine Ergebnisseite neu laden, um alle anderen mit auszusperren.
Zu viel Vertrauen ist noch problematischer. Wenn ein öffentlicher Bereich eingetragen ist, kann jeder Besucher seinen eigenen X-Forwarded-For-Header senden und für jede Anfrage eine neue Identität auswählen. Dadurch wird der Limiter für jeden deaktiviert, der dieses Verhalten kennt. Tragen Sie nur die Adresse ein, von der aus Ihr eigener Proxy Verbindungen herstellt. In Docker ist das normalerweise ein Bridge-Netzwerk innerhalb von 172.16.0.0/12. Diese Zeile ist standardmäßig auskommentiert.
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]Der Proxy muss die Header ebenfalls senden. Nginx fügt sie nicht automatisch hinzu:
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 und Traefik setzen die weitergeleiteten Header für Sie. Bei diesen Proxys müssen Sie daher nur die trusted_proxies-Hälfte der Konfiguration erledigen. Die jeweiligen Vor- und Nachteile werden unter Auswahl eines Reverse Proxys für einen selbst gehosteten Dienst erläutert. Um beide Konfigurationen zu überprüfen, aktivieren Sie debug, führen Sie einmal über Ihr Smartphone im Mobilfunknetz eine Suche durch und prüfen Sie, ob im Logeintrag das Netzwerk die Adresse Ihres Smartphones und nicht die des Proxys enthält.
Ihr Agent erhält vier API-Anfragen pro Stunde
Die JSON-Ausgabe ist standardmäßig deaktiviert. Ein Agent muss sie daher aktivieren:
search:
formats:
- html
- jsonLesen Sie die Tabellenzeile jetzt erneut. Jede Anfrage, die ein anderes Format als HTML anfordert, wird in einem eigenen Zeitfenster gezählt: 4 Anfragen pro 1 hour, je Adresse. Ein Rechercheagent verbraucht dieses Kontingent mit einer einzigen Aufgabe. Jeder weitere Aufruf führt danach zu 429. Das Limit zu erhöhen ist keine Option, weil die Anzahl im Quellcode festgelegt ist.
Die saubere Lösung besteht darin, dem Limiter mitzuteilen, dass dieser Client kein unbekannter Client ist. Fügen Sie seine Adresse in limiter.toml zur Allowlist hinzu:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip hat Vorrang vor allen anderen Methoden. Ein Client in der Allowlist überspringt daher auch die Header-Prüfungen, und ein einfacher curl-Aufruf funktioniert. Halten Sie den Bereich so klein wie möglich. Bevorzugen Sie ein VPN-Subnetz oder ein Container-Netzwerk gegenüber einem routbaren Netzwerk. Die andere saubere Lösung besteht darin, den Agenten vollständig aus dem öffentlichen Pfad herauszuhalten. Richten Sie ihn auf die Containeradresse im internen Netzwerk. Dort sehen der Proxy und sein Limiter den Datenverkehr nicht. Die Einrichtung wird unter einem SearXNG-Suchskill für einen KI-Agenten beschrieben.
Vermeiden Sie es, einen Agenten auf eine öffentliche Instanz zu verweisen, die von jemand anderem betrieben wird. Dadurch wird die IP-Adresse eines freiwilligen Betreibers am schnellsten von vorgeschalteten Suchmaschinen blockiert. Aus diesem Grund ist das JSON-Format standardmäßig deaktiviert.
Wenn die Engines Sie stattdessen blockieren
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"
}
]Wenn eine Engine mit einem eigenen 429-Fehler oder mit einer CAPTCHA-Seite antwortet, löst SearXNG eine benannte Ausnahme aus und fragt diese Engine eine Zeit lang nicht mehr ab. Eine Antwort mit „Too Many Requests“ führt zu einer Sperre von 3600 Sekunden. Eine normale CAPTCHA- oder „Access Denied“-Antwort führt zu einer Sperre für 1 day. Ein über Cloudflare ausgeliefertes CAPTCHA führt zu einer Sperre für 15 days, der längsten Standardeinstellung in dieser Liste. Diese Antwort bedeutet, dass die Sperre am Edge erfolgt und erneute Anfragen nicht helfen. Welche der drei CAPTCHA-Zeilen angezeigt wird, bestimmt, was als Nächstes sinnvoll ist. Für CAPTCHA-Fehler gibt es eigene Maßnahmen, sobald Sie wissen, welche Ausnahme Ihre Instanz protokolliert hat.
Für normale Fehler gelten andere Einstellungen. Ein Timeout oder ein Parserfehler setzt die Engine für eine kurze, aus search.ban_time_on_fail abgeleitete Zeit aus. Dieser Wert beträgt standardmäßig 5 Sekunden und wird durch search.max_ban_time_on_fail auf 120 Sekunden begrenzt. Eine langsame Engine erholt sich daher innerhalb weniger Minuten selbstständig, während eine blockierte Engine stundenlang nicht verfügbar ist. Dieser Unterschied erklärt ein Symptom, das häufig als zufällig wahrgenommen wird: Zuerst sind die Ergebnisse in Ordnung, dann verschwinden die Ergebnisse einer Engine für den restlichen Nachmittag.
Beheben Sie Timeouts, bevor Sie die Ursache anderen Faktoren zuschreiben. Der Standardwert request_timeout beträgt 2.0 Sekunden. Das ist für einen kleinen VPS, der weit vom nächstgelegenen Edge-Server einer Engine entfernt steht, knapp bemessen.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout ist der Standardwert für jede Engine, max_request_timeout ist die Obergrenze, und eine einzelne Engine kann einen eigenen Wert für timeout haben. Höhere Werte erhöhen die Latenz beim Laden der Seite, führen aber zu weniger Fehlern. Erhöhen Sie den Wert daher in Schritten von einer halben Sekunde und beobachten Sie /stats/errors, statt direkt auf 10 zu springen.
Wenn eine Engine Ihre Adresse tatsächlich blockiert, entfernen Sie sie. Jede Suche wartet auf ihre langsamste Engine. Eine dauerhaft ausgesetzte Engine erhöht daher die Latenz und liefert keine Ergebnisse.
use_default_settings:
engines:
remove:
- googleÜbernehmen Sie die Änderungen mit docker compose restart searxng-core. Führen Sie anschließend einige Suchvorgänge aus und laden Sie /stats/errors neu. Wenn die Seite nach fünf Minuten tatsächlicher Nutzung leer bleibt, wurde die Änderung wirksam.
Eine IP-Adresse aus einem Rechenzentrum wird als Bot behandelt
Ihre VPS-Adresse gehört zu einem Hosting-Adressbereich. Große Suchmaschinen bewerten solche Bereiche als automatisierten Datenverkehr. Einige zeigen für jede Anfrage von einer solchen Adresse ein CAPTCHA an. Dabei spielt es keine Rolle, wie unauffällig die Header sind oder wie langsam die Anfragen erfolgen. Keine Einstellung in settings.yml ändert diese Bewertung. Dass die Suchmaschinen Ihren Server statt der Person sehen, die die Anfrage eingibt, ist außerdem der gesamte Datenschutzkompromiss des Self-Hostings. Wie viel SearXNG tatsächlich verbirgt sollten Sie lesen, bevor Sie von einem größeren Schutzumfang ausgehen.
Sie können jedoch ändern, welche Suchmaschinen Sie abfragen und ob Ihre Instanz öffentlich gelistet ist. Eine private Instanz, die von einem Haushalt verwendet wird, löst nur selten solche Prüfungen aus. Eine öffentliche Instanz auf einer Hosting-IP wird bei den strengsten Suchmaschinen regelmäßig gesperrt. Das ist der normale Zustand der Software und kein Fehler in Ihrer Konfiguration. SearXNG kann Suchmaschinenanfragen mit outgoing.proxies oder outgoing.using_tor_proxy über einen Proxy weiterleiten. Dadurch wird der Datenverkehr von einer anderen Adresse gesendet. Exit-Nodes und günstige Proxy-Pools werden schlechter bewertet als Hosting-Adressbereiche. Rechnen Sie daher damit, dass sich die Ergebnisse dadurch verschlechtern.
Überwachen Sie die Instanz, damit Sie Probleme zuerst erkennen
SearXNG antwortet auf seinem Port, auch wenn alle Engines angehalten sind. Eine Uptime-Prüfung, die nur den Statuscode überwacht, bleibt deshalb grün, obwohl die Instanz keine Ergebnisse zurückgibt. Prüfen Sie stattdessen den Inhalt: Führen Sie eine echte Suche aus und suchen Sie im Antworttext nach einem erwarteten Wort. Keyword-Überwachung mit Uptime Kuma erledigt genau das ohne zusätzliche Werkzeuge. Überwachen Sie /stats/errors außerdem nach jedem Versionssprung, da sich das HTML der Engines ändern kann und ein Parser dadurch ohne Beteiligung eines Rate-Limits ausfällt.
FAQ
Warum gibt SearXNG jedem Besucher den Status 429 zurück, nachdem ich es hinter einen Reverse Proxy gestellt habe?
Weil der Limiter den Proxy als Client zählt. SearXNG liest X-Forwarded-For nur dann aus, wenn die Verbindungsadresse in trusted_proxies unter /etc/searxng/limiter.toml aufgeführt ist. Andernfalls teilen sich alle Besucher einen Zähler und überschreiten gemeinsam die Grenze von 150 requests in 10 minutes. Fügen Sie die Adresse hinzu, von der aus Ihr Proxy die Verbindung herstellt. In Docker ist das normalerweise der Bridge-Bereich 172.16.0.0/12. Stellen Sie außerdem sicher, dass der Proxy X-Real-IP und X-Forwarded-For sendet. Führen Sie niemals einen Bereich auf, den Sie nicht kontrollieren. In einem vertrauenswürdigen Netzwerk kann jeder Besucher diesen Header setzen und für jede Anfrage eine neue Identität wählen.
Wie viele API requests pro hour erlaubt der SearXNG-Limiter?
Vier pro IP-Adresse und hour. Jede Anfrage, die ein anderes Format als HTML anfordert, wird in einem separaten one hour window gezählt. Dieses Limit ist unter searx/botdetection/ip_limit.py und nicht unter limiter.toml festgelegt. Es kann daher nicht über die Konfiguration erhöht werden. Ein Agent oder ein Script überschreitet es innerhalb eines einzelnen Tasks. Fügen Sie die Adresse des Clients unter pass_ip in limiter.toml hinzu. Alternativ können Sie die Instanz über ein internes Netzwerk erreichen, in dem der Limiter die Anfrage nicht sieht.
Warum bleiben meine Suchergebnisse ohne Fehler 429 leer?
Die Engines verweigern Ihren Server, nicht Ihren Benutzern den Zugriff. Öffnen Sie /stats/errors auf Ihrer eigenen Instanz. Dort werden die fehlgeschlagenen Engines und die jeweiligen Gründe aufgeführt. Ein CAPTCHA- oder Access-denied-Eintrag bedeutet, dass die Engine die IP-Adresse Ihres Servers blockiert hat. SearXNG setzt die Engine anschließend aus: eine hour nach einer Antwort mit too many requests und einen Tag nach einem CAPTCHA. Keine lokale Einstellung hebt eine upstreamseitige Blockierung auf. Entfernen Sie daher die Engines, die Ihre Adresse blockieren, und behalten Sie die Engines, die antworten.
Sollte ich den Limiter auf einer privaten Instanz aktivieren?
Wenn nur Sie auf die Instanz zugreifen, lassen Sie limiter: false deaktiviert. Der Limiter fügt eine Valkey-Abhängigkeit hinzu und blockiert Ihre eigenen Scripts. Außerdem schützt er vor Traffic, den es bei Ihnen nicht gibt. Aktivieren Sie ihn, sobald die Instanz eine öffentliche Adresse erhält, zusammen mit public_instance: true. Diese Kombination ist beabsichtigt: Wenn public_instance: true aktiviert ist und Valkey nicht funktioniert, beendet sich der Prozess mit Status 1, statt ungeschützt weiterzulaufen.