Résoudre les erreurs 429 et les limites SearXNG
SearXNG renvoie 429 pour deux raisons : son propre limiteur ou le blocage de l’IP par un moteur. Lisez les logs pour appliquer la bonne correction.
Pourquoi SearXNG renvoie des erreurs 429
Une instance SearXNG auto-hébergée renvoie des erreurs 429 pour deux raisons distinctes, et la limite de débit que vous devez corriger n’est généralement pas celle que vous pensez. La première raison est locale : le limiteur propre à SearXNG a considéré qu’une requête venait d’un bot et a répondu Too Many Requests avec le statut 429. La seconde est externe : un moteur de recherche a refusé l’adresse IP de votre serveur. Pour vos utilisateurs, cela se manifeste par une page de résultats incomplète, et non par une erreur 429.
Ces deux cas n’ont pas la même solution. Le limiteur est sous votre contrôle, vous pouvez donc le modifier. Le blocage externe se produit du côté de Google ; aucune modification de votre settings.yml ne le supprimera. Les journaux permettent de déterminer le cas rencontré en environ une minute. Commencez donc par les consulter.
Ce guide suppose que vous avez effectué l’installation en conteneur décrite dans une instance SearXNG auto-hébergée sur votre propre VPS. Tous les noms de paramètres ci-dessous proviennent de la documentation et du code source upstream actuels, vérifiés en août 2026.
Lisez le journal avant de modifier un paramètre
Reproduisez le problème avec une fenêtre de journal ouverte.
cd ./searxng/
docker compose logs -f searxng-coreLes messages du limiter proviennent du logger nommé searx.limiter et indiquent une adresse IP. Un blocage par la blocklist apparaît sous la forme BLOCK 203.0.113.10: matched BLOCKLIST, tandis qu’un passage par l’allowlist apparaît sous la forme PASS 203.0.113.10: matched PASSLIST. Si le limiter ne peut pas joindre son compteur, le journal indique The limiter requires Valkey, please consult the documentation. Cela signifie qu’aucune requête n’est comptabilisée.
Chaque vérification de bot est journalisée au niveau debug. Vous ne la verrez donc pas par défaut. Activez debug pour un test dans settings.yml :
general:
debug: trueLe journal ajoute alors des lignes de la forme NOT OK (http_accept_language) à côté du réseau client et indique la vérification qui a échoué. Désactivez ensuite debug, car la documentation amont déconseille d’exécuter une instance en production avec debug activé.
Les erreurs du moteur sont très différentes. Elles indiquent un moteur au lieu d’une adresse IP. La plus courante est un timeout :
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)Une page est également disponible à ce sujet. Lorsque enable_metrics conserve sa valeur par défaut, true, votre instance enregistre les erreurs des moteurs dans /stats/errors, et /preferences indique quels moteurs répondent actuellement. Si /stats/errors est plein et que le journal ne contient aucune ligne searx.limiter, le limiter n’est pas en cause.
Épinglez la version avant tout diagnostic
La configuration du conteneur fournie par le projet amont repose sur deux fichiers.
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 .envLe fichier Compose récupère docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. Une variable non définie signifie latest, et latest signifie que l’instance change lors du prochain docker compose pull. Un paramètre qui fonctionnait la semaine dernière peut donc ne plus correspondre au code qui le lit. Les tags SearXNG contiennent une date et un commit. En août 2026, le tag indiqué dans le .env.example amont est 2026.3.25-541c6c3cb. Définissez donc une valeur réelle dans .env :
SEARXNG_VERSION=2026.3.25-541c6c3cbConsultez les tags publiés et épinglez la release que vous avez réellement testée. Effectuez ensuite le diagnostic sur une cible fixe. Le même fichier .env contient votre clé secrète. Consultez donc le fonctionnement des fichiers env et des secrets dans Docker Compose avant de valider ce répertoire dans un dépôt.
Le limiteur a besoin de Valkey pour fonctionner
Le limiteur compte les requêtes par client. Ces compteurs doivent être partagés entre les processus worker. Ce stockage est assuré par Valkey, le fork maintenu de Redis. Les anciennes guides SearXNG appellent ce paramètre redis:. Les versions actuelles lisent valkey:. Reprenez donc le nom de clé dans la documentation actuelle, et non dans une ancienne publication.
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0Le fichier compose fourni en amont lance déjà un service searxng-valkey basé sur l’image docker.io/valkey/valkey:9-alpine. Ce nom d’hôte est donc résolu sur le réseau compose. Vous pouvez également définir la même valeur avec la variable d’environnement SEARXNG_VALKEY_URL. Une URL de socket Unix (unix:///path/to/socket.sock?db=0) fonctionne lorsque SearXNG et Valkey partagent le même hôte.
Le comportement lorsque le stockage est absent dépend d’une autre clé. Avec public_instance: false, le limiteur journalise l’erreur Valkey et abandonne. L’instance continue donc de traiter les requêtes sans aucune limitation de débit. Avec public_instance: true, le processus appelle plutôt sys.exit(1). Une instance ouverte dont la protection contre les bots est défaillante collecte en effet des CAPTCHA (tests publics complètement automatisés pour distinguer les ordinateurs des humains) auprès de chaque moteur en moins d’une journée. Si un conteneur redémarre en boucle juste après la définition de public_instance: true, c’est probablement le problème. La dernière ligne avant chaque arrêt indique le nom de Valkey.
Ce que le limiteur compte réellement
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"
}
]Un client normal peut envoyer 15 requêtes pendant une fenêtre de rafale de 20 secondes et 150 pendant une fenêtre de 10 minutes. Dès qu’une requête est marquée comme suspecte, le même client est limité à 2 par fenêtre de rafale. La dernière ligne est la plus stricte : après 3 requêtes marquées dans une fenêtre de 30 jours, cette adresse est redirigée vers la page d’accueil au lieu d’effectuer la recherche, et le journal indique BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).
Ces nombres sont des constantes dans searx/botdetection/ip_limit.py. Ce ne sont pas des paramètres, et limiter.toml ne les expose pas. Pour les modifier, il faut donc éditer le code source. /etc/searxng/limiter.toml contrôle les préfixes d’adresses utilisés pour regrouper les clients, la liste des proxies de confiance, le contrôle facultatif du link token, ainsi que les listes d’autorisation et de blocage.
Une requête est marquée comme suspecte à la suite de contrôles des en-têtes. Chaque contrôle porte un nom que vous verrez dans le debug log :
http_accept: l’en-têteAcceptne contient pastext/html.http_accept_encoding: l’en-têteAccept-Encodingne désigne nigzipnideflate.http_accept_language: aucun en-têteAccept-Languagen’est présent.http_connection: l’en-têteConnectioncontient la valeurclose.http_user_agent:User-Agentest absent ou correspond à un pattern de bot connu.http_sec_fetch: l’en-têteSec-Fetch-ModeouSec-Fetch-Destne correspond pas à ce qu’envoie un navigateur.
Un navigateur envoie tous ces éléments. Un appel curl simple n’en envoie presque aucun. Une requête de test écrite manuellement est donc marquée dès la première tentative, alors que la même recherche fonctionne dans un onglet de navigateur. C’est pourquoi le message « cela fonctionne dans mon navigateur, mais mon script reçoit une réponse 429 » est le résultat normal, et non un comportement mystérieux.
Derrière un reverse proxy, le limiter bloque tout le monde en même temps
C’est la manière la plus courante de casser une instance fonctionnelle. SearXNG récupère l’adresse du client à partir de la première IP non fiable dans X-Forwarded-For, utilise X-Real-IP en secours, puis l’adresse qui a ouvert la connexion en dernier recours. La directive trusted_proxies dans limiter.toml détermine si ces en-têtes sont pris en compte.
Si l’adresse de votre proxy ne figure pas dans cette liste, les en-têtes sont ignorés et chaque visiteur arrive avec l’adresse du proxy. Ils partagent alors un même compteur. Lorsque le total dépasse 150 requêtes en 10 minutes, tout le site est bloqué. Un utilisateur qui recharge plusieurs fois une page de résultats peut ainsi bloquer tous les autres.
Faire trop confiance est encore pire. Si une plage d’adresses publiques est indiquée, n’importe quel visiteur peut envoyer son propre en-tête X-Forwarded-For et choisir une nouvelle identité à chaque requête. Le limiter est alors désactivé pour toute personne qui sait exploiter ce comportement. Indiquez uniquement l’adresse depuis laquelle votre propre proxy se connecte. Avec Docker, il s’agit généralement d’un réseau bridge dans 172.16.0.0/12, et cette ligne est commentée par défaut.
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]Le proxy doit également envoyer les en-têtes. Nginx n’en ajoute aucun automatiquement :
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 et Traefik définissent les en-têtes forwarded automatiquement. Avec ces solutions, vous devez donc uniquement configurer la partie trusted_proxies. Les compromis sont présentés dans choisir un reverse proxy pour un service auto-hébergé. Pour vérifier l’une ou l’autre configuration, activez debug, effectuez une recherche depuis votre téléphone avec les données mobiles, puis vérifiez que le réseau indiqué dans la ligne du journal correspond à l’adresse de votre téléphone et non à celle du proxy.
Votre agent reçoit quatre requêtes API par heure
La sortie JSON est désactivée par défaut. Il faut donc l’ajouter à l’agent :
search:
formats:
- html
- jsonRelisez maintenant la ligne correspondante du tableau. Toute requête demandant un format autre que HTML est comptabilisée dans sa propre fenêtre : 4 requêtes par 1 hour, pour chaque adresse. Un agent de recherche atteint cette limite avec une seule tâche, puis chaque appel suivant renvoie une erreur 429. Il n’est pas possible d’augmenter cette limite, car sa valeur est définie dans le code source.
La solution consiste à indiquer au limiter que ce client est connu. Ajoutez son adresse à la pass list dans limiter.toml :
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip est prioritaire sur toutes les autres méthodes. Un client placé sur la allowlist ignore donc aussi les vérifications d’en-têtes, et un simple appel curl fonctionne. Limitez la plage autant que possible. Préférez un sous-réseau VPN ou un réseau de conteneurs à toute adresse routable. L’autre solution consiste à empêcher complètement l’agent d’emprunter le chemin public : dirigez-le vers l’adresse du conteneur sur le réseau interne. Le proxy et son limiter ne verront alors jamais le trafic. La configuration correspondante est présentée dans donner à un agent IA une compétence de recherche SearXNG.
Évitez de diriger un agent vers une instance publique exploitée par un tiers. C’est le moyen le plus rapide de faire bloquer l’adresse IP d’un bénévole par les moteurs en amont. C’est aussi la raison pour laquelle le format JSON est désactivé par défaut.
Quand les moteurs vous bloquent à leur tour
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"
}
]Lorsqu’un moteur renvoie son propre code 429 ou une page CAPTCHA, SearXNG lève une exception nommée et cesse de l’interroger pendant un certain temps. Une réponse indiquant trop de requêtes le suspend pendant 3600 secondes. Une réponse CAPTCHA simple ou une réponse d’accès refusé le suspend pendant 1 day. Un CAPTCHA servi par Cloudflare le suspend pendant 15 days, soit la durée par défaut la plus longue de la liste, car cette réponse signifie que le blocage se situe en amont et que réessayer ne servira à rien.
Les échecs ordinaires utilisent d’autres paramètres. Un timeout ou une erreur d’analyse suspend le moteur pendant une courte durée calculée à partir de search.ban_time_on_fail, qui vaut 5 secondes par défaut et est plafonnée par search.max_ban_time_on_fail à 120 secondes. Ainsi, un moteur lent redevient disponible automatiquement en quelques minutes, tandis qu’un moteur bloqué reste indisponible pendant des heures. Cette différence explique un symptôme souvent décrit comme aléatoire : les résultats sont corrects, puis ceux d’un moteur disparaissent pour le reste de l’après-midi.
Il est préférable de corriger les timeouts avant d’incriminer qui que ce soit. La valeur par défaut de request_timeout est de 2.0 secondes, ce qui est court pour un petit VPS éloigné du serveur edge le plus proche d’un moteur.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout est la valeur par défaut pour chaque moteur, max_request_timeout est la limite maximale, et un moteur peut avoir sa propre valeur timeout. Augmenter ces valeurs augmente la latence des pages, mais réduit le nombre d’échecs. Procédez donc par paliers d’une demi-seconde et surveillez /stats/errors au lieu de passer directement à 10.
Si un moteur bloque réellement votre adresse, supprimez-le. Chaque recherche attend son moteur le plus lent. Conserver un moteur suspendu en permanence augmente donc la latence sans renvoyer de résultats.
use_default_settings:
engines:
remove:
- googleAppliquez les modifications avec docker compose restart searxng-core, puis effectuez quelques recherches et rechargez /stats/errors. Si la page reste vide après cinq minutes d’utilisation réelle, la modification a fonctionné.
Une adresse IP de datacentre sera traitée comme celle d’un bot
L’adresse de votre VPS appartient à une plage d’hébergement, et les grands moteurs classent ces plages comme automatisées. Certains présentent un CAPTCHA pour chaque requête provenant d’une telle adresse, quels que soient la courtoisie des en-têtes ou le faible rythme des requêtes. Aucun réglage de settings.yml ne modifie cette évaluation.
Vous pouvez en revanche choisir les moteurs interrogés et décider si votre instance est publique. Une instance privée utilisée par un seul foyer déclenche rarement des protections. Une instance publique sur une adresse IP d’hébergement accumulera des suspensions auprès des moteurs les plus stricts. Il s’agit du comportement normal du logiciel, et non d’un défaut de votre configuration. SearXNG peut faire transiter les requêtes vers les moteurs par un proxy avec outgoing.proxies ou outgoing.using_tor_proxy, ce qui utilise une autre adresse pour le trafic. Les nœuds de sortie et les pools de proxies bon marché sont moins bien notés que les plages d’hébergement. Ce changement risque donc de dégrader les résultats.
Surveillez l’instance pour détecter les problèmes en premier
SearXNG répond sur son port même lorsque tous les moteurs sont suspendus. Un contrôle de disponibilité qui vérifie uniquement le code d’état reste donc au vert alors que l’instance ne renvoie aucun résultat. Vérifiez plutôt le contenu : lancez une vraie recherche et recherchez dans le corps de la réponse un mot dont vous attendez la présence. Surveillance par mot-clé avec Uptime Kuma fait exactement cela, sans outil supplémentaire. Surveillez également /stats/errors après chaque mise à niveau de version, car le HTML des moteurs change et un parser peut alors cesser de fonctionner sans qu’aucune limitation de débit n’intervienne.
FAQ
Pourquoi SearXNG renvoie-t-il 429 à chaque visiteur après son placement derrière un reverse proxy ?
Parce que le limiter compte le proxy comme client. SearXNG ne lit X-Forwarded-For que lorsque l’adresse de connexion figure dans trusted_proxies de /etc/searxng/limiter.toml. Si elle n’y figure pas, tous les visiteurs partagent le même compteur et dépassent ensemble le seuil de 150 requêtes en 10 minutes. Ajoutez l’adresse depuis laquelle votre proxy se connecte. Dans Docker, il s’agit généralement de la plage de bridge 172.16.0.0/12. Vérifiez également que le proxy envoie X-Real-IP et X-Forwarded-For. Ne référencez jamais une plage que vous ne contrôlez pas, car un réseau de confiance permet à n’importe quel visiteur de définir cet en-tête et de choisir une nouvelle identité à chaque requête.
Combien de requêtes API par heure le limiter de SearXNG autorise-t-il ?
Quatre par adresse IP et par heure. Toute requête demandant un format autre que HTML est comptabilisée dans une fenêtre distincte d’une heure. Cette limite est définie dans searx/botdetection/ip_limit.py et non dans limiter.toml. Elle ne peut donc pas être augmentée dans la configuration. Un agent ou un script atteint cette limite en une seule tâche. Ajoutez l’adresse du client à pass_ip dans limiter.toml, ou accédez à l’instance via un réseau interne où le limiter ne voit jamais la requête.
Pourquoi mes résultats de recherche sont-ils vides sans erreur 429 ?
Les moteurs refusent votre serveur, pas vos utilisateurs. Ouvrez /stats/errors sur votre propre instance. Ce fichier indique quel moteur a échoué et pour quelle raison. Une entrée CAPTCHA ou accès refusé signifie que ce moteur a bloqué l’adresse IP de votre serveur. SearXNG suspend ensuite le moteur pendant une heure après une réponse indiquant trop de requêtes, et pendant un jour après un CAPTCHA. Aucun réglage local ne peut lever un blocage en amont. Supprimez donc les moteurs qui bloquent votre adresse et conservez ceux qui répondent.
Dois-je activer le limiter sur une instance privée ?
Si personne d’autre que vous n’accède à l’instance, laissez limiter: false. Il ajoute une dépendance à Valkey et bloque vos propres scripts, tout en vous protégeant contre un trafic que vous ne recevez pas. Activez-le dès que l’instance reçoit une adresse publique, avec public_instance: true. Cette combinaison est volontaire : avec public_instance: true et sans Valkey opérationnel, le processus se termine avec le statut 1 au lieu de fonctionner sans protection.