SearXNG : corriger les erreurs 429 et le rate limit
Une erreur 429 SearXNG vient du limiteur local ou d’un moteur qui bloque votre IP. Identifiez la cause dans les logs et appliquez 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 intégré de SearXNG a considéré qu’une requête provenait 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. Ce blocage se manifeste chez vos utilisateurs 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 vous appartient, vous pouvez donc le modifier. Le blocage externe se produit du côté de Google. Rien dans votre settings.yml ne le lèvera. Les journaux indiquent lequel des deux cas vous rencontrez en environ une minute. Commencez donc par les consulter.
Ce guide part du principe que vous utilisez 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 limiteur 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 limiteur ne peut pas accéder à 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 plutôt qu’une adresse IP. L’erreur la plus fréquente est un timeout :
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)Une page est également consacrée à ce sujet. Avec enable_metrics à sa valeur par défaut true, votre instance journalise les erreurs du moteur au niveau /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 limiteur n’est pas en cause.
Figez la version avant tout diagnostic
La configuration du conteneur fournie par le projet amont se compose de 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 au 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. Le tag indiqué dans le .env.example amont en août 2026 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 figez 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 limiter a besoin de Valkey, sinon il ne s’exécute pas
Le limiteur comptabilise 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 anciens guides SearXNG appellent ce paramètre redis:. Les releases actuelles lisent valkey:. Copiez donc le nom de clé depuis la documentation actuelle, et non depuis un ancien article. Certaines de ces pages sont encore plus anciennes et décrivent Searx plutôt que SearXNG. Il s’agit d’un autre code source, avec un limiteur différent. Vérifiez donc pour lequel des deux projets la page a été rédigée avant d’en copier un bloc de configuration.
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 avec l’image docker.io/valkey/valkey:9-alpine. Ce nom d’hôte est donc résolu sur le réseau Compose. Vous pouvez 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 en l’absence du store dépend d’une autre clé. Avec public_instance: false, le limiter journalise l’erreur Valkey puis s’arrête. L’instance continue donc à répondre sans aucune limitation de débit. Avec public_instance: true, le processus appelle plutôt sys.exit(1), car une instance ouverte dont la protection contre les bots est défaillante collecte des CAPTCHA (tests publics de Turing complètement automatisés pour distinguer les ordinateurs des humains) auprès de chaque moteur en une journée. Si un conteneur redémarre en boucle juste après la définition de public_instance: true, c’est ce qui se produit. La dernière ligne avant chaque arrêt mentionne 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 reçoit 15 requêtes dans une fenêtre de rafale de 20 secondes et 150 dans une fenêtre de 10 minutes. Dès qu’une requête est signalée comme suspecte, le même client passe à 2 par fenêtre de rafale. La dernière ligne est la plus stricte : après 3 requêtes signalé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 contient 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. En revanche, /etc/searxng/limiter.toml contrôle bien les préfixes d’adresse utilisés pour regrouper les clients, la liste des proxies de confiance, la vérification facultative du token de lien, ainsi que les listes d’autorisation et de blocage.
Une requête est signalée comme suspecte lors des vérifications des en-têtes. Chaque vérification possède un nom que vous retrouverez dans le debug log :
http_accept: l’en-têteAcceptne contient pastext/html.http_accept_encoding: l’en-têteAccept-Encodingne contient 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 motif 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 signalée dès la première tentative, alors que la même recherche fonctionne dans un onglet du navigateur. C’est pourquoi le message « cela fonctionne dans mon navigateur, mais mon script reçoit une réponse 429 » est un 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 dans la première IP non approuvée de X-Forwarded-For, utilise ensuite X-Real-IP, puis l’adresse qui a ouvert la connexion. La décision de faire confiance ou non à ces en-têtes dépend de trusted_proxies dans limiter.toml.
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 le même compteur. L’ensemble du site est bloqué dès que le total dépasse 150 requêtes en 10 minutes. Un utilisateur qui recharge plusieurs fois une page de résultats peut ainsi bloquer tout le monde.
Faire trop confiance est encore plus risqué. Si une plage d’adresses publique 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 logiciels, il ne vous reste donc qu’à 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 l’adresse indiquée dans la ligne du log est celle de votre téléphone, et non celle du proxy.
Votre agent dispose de 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, par adresse. Un agent de recherche consomme cette limite avec une seule tâche, puis chaque appel suivant renvoie une erreur 429. Augmenter la limite n’est pas possible, car sa valeur est définie dans le code source.
La solution consiste à indiquer au limiter que ce client est autorisé. Ajoutez son adresse à la liste d’autorisation dans limiter.toml :
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip a priorité sur toutes les autres méthodes. Un client de la liste d’autorisation ignore donc aussi les contrôles 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 plage routable. L’autre solution consiste à ne jamais exposer l’agent au réseau public : dirigez-le vers l’adresse du conteneur sur le réseau interne, où le proxy et son limiter ne voient jamais le trafic. La configuration correspondante est décrite dans donner à un agent IA une fonctionnalité de recherche SearXNG.
Évitez en revanche de diriger un agent vers une instance publique géré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
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 répond avec son propre code 429 ou avec 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. Un 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 indique que le blocage se situe en périphérie et que réessayer ne servira à rien. La ligne CAPTCHA sur laquelle vous êtes arrivé parmi les trois change les mesures qui valent la peine d’être tentées ensuite, et les erreurs CAPTCHA ont leur propre ensemble de corrections une fois que vous savez quelle exception votre instance a enregistrée.
Les erreurs 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. Un moteur lent récupère donc 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 de chercher un responsable. La valeur par défaut de request_timeout est de 2.0 secondes, ce qui est peu pour un petit VPS éloigné du serveur edge le plus proche du 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 tous les moteurs, max_request_timeout est la limite supérieure, et un moteur peut définir sa propre valeur timeout. Augmenter ces valeurs accroît la latence des pages, mais réduit le nombre d’échecs. Procédez donc par incréments 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 et ne renvoie aucun résultat.
use_default_settings:
engines:
remove:
- googleAppliquez les modifications avec docker compose restart searxng-core, exécutez ensuite quelques recherches et rechargez /stats/errors. Si la page est vide après cinq minutes d’utilisation réelle, la modification a fonctionné.
Une adresse IP de datacentre sera considéré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 affichent un CAPTCHA pour chaque requête provenant d’une telle adresse, quels que soient les en-têtes utilisés ou la lenteur du rythme des requêtes. Aucun paramètre de settings.yml ne modifie ce classement. Le fait que les moteurs voient votre serveur au lieu de la personne qui saisit la requête constitue aussi l’essentiel du compromis de confidentialité lié à l’auto-hébergement. Lisez ce que SearXNG masque réellement avant de supposer qu’il protège davantage d’éléments.
Vous pouvez modifier les moteurs interrogés et décider si votre instance est publiée. Une instance privée utilisée par un seul foyer déclenche rarement ces protections. Une instance publique hébergée sur une adresse IP d’hébergeur accumulera les suspensions sur les moteurs les plus stricts. Il s’agit du fonctionnement normal du logiciel, et non d’une erreur de configuration. SearXNG peut acheminer les requêtes vers les moteurs via un proxy avec outgoing.proxies ou outgoing.using_tor_proxy, ce qui transfère le trafic vers une autre adresse. Les nœuds de sortie et les pools de proxies bon marché sont plus mal classés que les plages d’hébergement. Attendez-vous donc à une dégradation des résultats après ce changement.
Surveillez l’instance pour détecter rapidement les problèmes
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 qui doit s’y trouver. Surveillance de mots-clés avec Uptime Kuma fait exactement cela, sans outil supplémentaire. Surveillez également /stats/errors après chaque mise à jour de version, car le HTML des moteurs peut changer et casser un analyseur sans qu’aucune limite de débit n’intervienne.
FAQ
Pourquoi SearXNG renvoie-t-il 429 à tous les visiteurs après son placement derrière un reverse proxy ?
Parce que le limiter considère le proxy comme le 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 la limite de 150 requêtes en 10 minutes. Ajoutez l’adresse depuis laquelle votre proxy se connecte. Avec Docker, il s’agit généralement de la plage 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. Un réseau de confiance permettrait à 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 qui demande 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 chaque moteur qui a échoué et la raison. Une entrée CAPTCHA ou access-denied 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 too-many-requests et pendant un jour après un CAPTCHA. Aucun réglage local ne lève 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 qui n’existe pas. Activez-le dès que l’instance reçoit une adresse publique, avec public_instance: true. Cette combinaison est intentionnelle : avec public_instance: true et sans Valkey fonctionnel, le processus se termine avec le statut 1 au lieu de fonctionner sans protection.