Cómo corregir los errores 429 de SearXNG
SearXNG muestra 429 por su limitador o por bloqueos de los motores. Aprende a distinguir ambos casos en el registro y aplica la corrección adecuada.
Por qué SearXNG devuelve errores 429
Una instancia de SearXNG autohospedada devuelve errores 429 por dos motivos independientes, y el límite de tasa que debe corregir normalmente no es el que supone. El primer motivo es local: el limitador propio de SearXNG determina que una solicitud procede de un bot y responde Too Many Requests con el estado 429. El segundo es ascendente: un motor de búsqueda rechaza la dirección IP de su servidor, lo que llega a sus usuarios como una página de resultados a la que le faltan elementos, no como un error 429.
Los dos casos no comparten ninguna solución. El limitador es suyo, así que puede cambiarlo. El bloqueo ascendente se produce en el lado de Google, por lo que nada de su settings.yml lo eliminará. El registro indica cuál de los dos casos tiene en aproximadamente un minuto, así que empiece por ahí.
Esta guía presupone la instalación en contenedor descrita en una instancia de SearXNG autohospedada en su propio VPS. Todos los nombres de configuración que aparecen a continuación proceden de la documentación y del código fuente ascendentes actuales, comprobados en agosto de 2026.
Lee el registro antes de cambiar un ajuste
Reproduce el problema con una ventana del registro abierta.
cd ./searxng/
docker compose logs -f searxng-coreLos mensajes del limitador proceden del registrador llamado searx.limiter y contienen una dirección IP. Un acierto de la lista de bloqueo aparece como BLOCK 203.0.113.10: matched BLOCKLIST, mientras que un acierto de la lista de permitidos aparece como PASS 203.0.113.10: matched PASSLIST. Si el limitador no puede acceder al almacén de contadores, el registro muestra The limiter requires Valkey, please consult the documentation. Esto significa que no se está contando ninguna petición.
Cada comprobación individual de un bot se registra en el nivel de depuración, por lo que no la verás de forma predeterminada. Activa la depuración para una sola prueba en settings.yml:
general:
debug: trueEl registro añade entonces líneas con el formato NOT OK (http_accept_language) junto a la red del cliente e indica qué comprobación falló. Desactiva la depuración después, porque el proyecto upstream indica que no se debe ejecutar una instancia desplegada con la depuración activada.
Los fallos del motor tienen un aspecto completamente distinto. Indican un motor en lugar de una IP, y el más habitual es un tiempo de espera agotado:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)También existe una página para esto. Con enable_metrics en su valor predeterminado de true, la instancia registra los errores del motor en /stats/errors, y /preferences muestra qué motores están respondiendo actualmente. Si /stats/errors está lleno y el registro no contiene líneas searx.limiter, el problema no está en el limitador.
Fije la versión antes de depurar nada
La configuración del contenedor proporcionada por el proyecto consta de dos archivos.
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 .envEl archivo de Compose extrae docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. Una variable no definida significa latest, y latest significa que la instancia cambia en el siguiente docker compose pull. Por tanto, un ajuste que funcionó la semana pasada puede dejar de coincidir con el código que lo lee. Las etiquetas de SearXNG incluyen una fecha y un commit. La etiqueta de ejemplo del .env.example original a fecha de agosto de 2026 es 2026.3.25-541c6c3cb, así que establezca una versión real en .env:
SEARXNG_VERSION=2026.3.25-541c6c3cbCompruebe las etiquetas publicadas y fije la versión que haya probado realmente. Después, depure con un objetivo fijo. El mismo archivo .env contiene su clave secreta. Lea cómo funcionan los archivos de entorno y los secretos en Docker Compose antes de confirmar ese directorio en cualquier repositorio.
El limitador necesita Valkey o no se ejecuta
El limitador cuenta las solicitudes de cada cliente, y esos contadores deben compartirse entre los procesos de trabajo. Ese almacén es Valkey, la bifurcación mantenida de Redis. Las guías antiguas de SearXNG llaman a este ajuste redis:. Las versiones actuales leen valkey:, así que copie el nombre de la clave de la documentación actual y no de una publicación antigua. Algunas de esas páginas se remontan aún más atrás y describen Searx en lugar de SearXNG. Son bases de código distintas y tienen limitadores diferentes. Por eso, determine para cuál de los dos proyectos se escribió la página antes de copiar un bloque de configuración.
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0El archivo compose del proyecto ya ejecuta un servicio searxng-valkey con la imagen docker.io/valkey/valkey:9-alpine, por lo que ese nombre de host se resuelve dentro de la red de compose. También puede establecer el mismo valor con la variable de entorno SEARXNG_VALKEY_URL. Una URL de socket Unix (unix:///path/to/socket.sock?db=0) funciona cuando SearXNG y Valkey comparten un host.
Lo que ocurre cuando falta el almacén depende de otra clave. Con public_instance: false, el limitador registra el error de Valkey y se detiene, por lo que la instancia continúa atendiendo solicitudes sin ningún límite de tasa. Con public_instance: true, el proceso llama a sys.exit(1) en su lugar, porque una instancia pública con la protección contra bots averiada recibe CAPTCHA (prueba de Turing pública y automatizada para distinguir ordenadores de personas) de todos los motores en un día. Si un contenedor se reinicia en bucle justo después de establecer public_instance: true, esta es la causa. La última línea antes de cada salida identifica a Valkey.
Qué cuenta realmente el limitador
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 cliente normal puede realizar 15 solicitudes dentro de una ventana de ráfaga de 20 segundos y 150 dentro de una ventana de 10 minutos. Cuando una solicitud se marca como sospechosa, el mismo cliente queda limitado a 2 por ventana de ráfaga. La última fila aplica la medida más estricta: después de 3 solicitudes marcadas dentro de una ventana de 30 días, esa dirección se redirige a la página de inicio en lugar de permitir la búsqueda, y el registro muestra BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).
Estos números son constantes en searx/botdetection/ip_limit.py. No son opciones de configuración y limiter.toml no las expone, por lo que cambiarlas requiere editar el código fuente. Lo que /etc/searxng/limiter.toml sí controla son los prefijos de dirección que se usan para agrupar clientes, la lista de proxies de confianza, la comprobación opcional del token del enlace y las listas de permitidos y bloqueados.
Una solicitud se marca como sospechosa mediante comprobaciones de cabeceras. Cada comprobación tiene un nombre que aparecerá en el registro de depuración:
http_accept: la cabeceraAcceptno contienetext/html.http_accept_encoding: la cabeceraAccept-Encodingno nombra nigzipnideflate.http_accept_language: no existe ninguna cabeceraAccept-Language.http_connection: la cabeceraConnectiontiene el valorclose.http_user_agent: faltaUser-Agento coincide con un patrón de bot conocido.http_sec_fetch: la cabeceraSec-Fetch-ModeoSec-Fetch-Destno tiene el formato que envía un navegador.
Un navegador envía todas estas cabeceras. Una llamada simple de curl no envía casi ninguna, por lo que una solicitud de prueba escrita manualmente se marca desde el primer intento, mientras que la misma búsqueda funciona en una pestaña del navegador. Por eso, «funciona en mi navegador, pero mi script recibe 429» es el resultado normal y no un misterio.
El limitador bloquea a todos a la vez detrás de un proxy inverso
Esta es la forma más habitual de romper una instancia que funcionaba. SearXNG obtiene la dirección del cliente de la primera IP no confiable de X-Forwarded-For, recurre a X-Real-IP y, si tampoco está disponible, usa la dirección que abrió la conexión. trusted_proxies en limiter.toml determina si se confía en esas cabeceras.
Si la dirección del proxy no aparece en esa lista, las cabeceras se ignoran y todos los visitantes llegan con la dirección del proxy. Por tanto, comparten un único contador y todo el sitio queda bloqueado cuando el total supera 150 solicitudes en 10 minutos. Un usuario que recargue una página de resultados varias veces puede dejar sin servicio a todos los demás.
Confiar demasiado es peor. Si se incluye un rango público, cualquier visitante puede enviar su propia cabecera X-Forwarded-For y elegir una identidad nueva para cada solicitud. Esto desactiva el limitador para cualquiera que sepa intentarlo. Incluya sólo la dirección desde la que se conecta su propio proxy. En Docker suele ser una red bridge dentro de 172.16.0.0/12, y esa línea viene comentada.
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]El proxy también debe enviar las cabeceras. Nginx no añade ninguna por sí solo:
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 y Traefik configuran las cabeceras reenviadas automáticamente, por lo que con ellos sólo debe configurar la parte trusted_proxies. Las ventajas y desventajas se explican en cómo elegir un proxy inverso para un servicio autohospedado. Para verificar cualquiera de las dos configuraciones, active debug, haga una búsqueda desde el teléfono usando datos móviles y confirme que la red de la línea del registro muestra la dirección del teléfono, no la del proxy.
Tu agente recibe cuatro solicitudes de API por hora
La salida JSON está deshabilitada de forma predeterminada, por lo que hay que añadirla al agente:
search:
formats:
- html
- jsonAhora vuelve a leer la fila del gráfico. Cada solicitud que pide un formato distinto de HTML se contabiliza en su propia ventana: 4 solicitudes por 1 hour, por dirección. Un agente de investigación agota ese límite en una sola tarea, y cada llamada posterior devuelve 429. Aumentar el límite no es una opción, porque el valor está definido en el código fuente.
La solución correcta es indicar al limitador que este cliente no es desconocido. Añade su dirección a la lista de excepciones en limiter.toml:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip tiene prioridad sobre cualquier otro método, por lo que un cliente incluido en la lista de permitidos también omite las comprobaciones de cabeceras y una llamada curl sin argumentos funciona. Mantén el rango lo más reducido posible y prefiere una subred VPN o una red de contenedores antes que cualquier red enrutable. La otra solución correcta es mantener al agente completamente fuera de la ruta pública: dirígelo a la dirección del contenedor en la red interna, donde el proxy y su limitador nunca ven el tráfico. La configuración correspondiente se explica en dar a un agente de IA una capacidad de búsqueda de SearXNG.
La opción que debes evitar es dirigir un agente a una instancia pública administrada por otra persona. Es la forma más rápida de conseguir que los motores externos bloqueen la dirección IP de un voluntario, y por eso el formato JSON está deshabilitado de forma predeterminada.
Cuando los motores te bloquean
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"
}
]Cuando un motor responde con su propio código 429 o muestra una página CAPTCHA, SearXNG genera una excepción con nombre y deja de consultar ese motor durante un tiempo. Una respuesta de demasiadas solicitudes lo suspende durante 3600 segundos. Una respuesta CAPTCHA normal o de acceso denegado lo suspende durante 1 day. Un CAPTCHA servido a través de Cloudflare lo suspende durante 15 days, el intervalo predeterminado más largo de la lista, porque esa respuesta indica que el bloqueo se produce en el perímetro y reintentar no ayudará. La fila de CAPTCHA en la que hayas terminado determina qué conviene probar después, y los errores CAPTCHA tienen su propio conjunto de soluciones cuando sabes qué excepción registró tu instancia.
Los fallos normales utilizan otros ajustes. Un tiempo de espera o un error de análisis suspende el motor durante un intervalo corto derivado de search.ban_time_on_fail, cuyo valor predeterminado es 5 segundos y cuyo límite es search.max_ban_time_on_fail, con 120 segundos. Por tanto, un motor lento se recupera por sí solo en un par de minutos, mientras que un motor bloqueado desaparece durante horas. Esta diferencia explica un síntoma que muchas personas describen como aleatorio: los resultados son correctos y, después, desaparecen los resultados de un motor durante el resto de la tarde.
Conviene corregir los tiempos de espera antes de culpar a nadie. El valor predeterminado de request_timeout es 2.0 segundos. Es un intervalo ajustado para un VPS pequeño situado lejos del servidor perimetral más cercano del motor.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout es el valor predeterminado para todos los motores, max_request_timeout es el límite máximo y un motor concreto puede tener su propio timeout. Aumentar estos valores intercambia más latencia por menos fallos. Haz cambios de medio segundo y observa /stats/errors en lugar de saltar directamente a 10.
Si un motor bloquea realmente tu dirección, elimínalo. Cada búsqueda espera al motor más lento. Mantener uno suspendido de forma permanente añade latencia y no devuelve resultados.
use_default_settings:
engines:
remove:
- googleAplica los cambios con docker compose restart searxng-core, ejecuta varias búsquedas y vuelve a cargar /stats/errors. Una página vacía después de cinco minutos de uso real indica que el cambio funcionó.
Una IP de un centro de datos se tratará como la de un bot
La dirección de tu VPS pertenece a un rango de alojamiento, y los grandes motores de búsqueda clasifican esos rangos como automatización. Algunos muestran un CAPTCHA para todas las solicitudes procedentes de una dirección de ese tipo, sin importar lo correctas que sean las cabeceras ni lo lento que sea el ritmo. Ningún ajuste de settings.yml cambia esa clasificación. Que los motores vean tu servidor en lugar de a la persona que escribe la consulta también forma parte del coste de privacidad que asumiste al alojarte por tu cuenta, y conviene leer cuánto oculta realmente SearXNG antes de suponer que ofrece una protección mayor.
Puedes cambiar los motores a los que envías las consultas y decidir si tu instancia aparece publicada. Una instancia privada usada por un solo hogar rara vez activa estos controles. Una instancia pública en una IP de alojamiento acumulará suspensiones en los motores más estrictos. Ese es el estado normal del software, no un fallo de configuración. SearXNG puede enviar las solicitudes a los motores a través de un proxy con outgoing.proxies o outgoing.using_tor_proxy, lo que desplaza el tráfico a otra dirección. Los nodos de salida y las redes de proxies económicos tienen una valoración peor que los rangos de alojamiento, así que es de esperar que ese cambio empeore los resultados.
Supervise la instancia para detectar los problemas cuanto antes
SearXNG responde en su puerto incluso cuando todos los motores están suspendidos. Por eso, una comprobación de disponibilidad que sólo supervise el código de estado permanece en verde mientras la instancia no devuelve resultados. Compruebe también el contenido: solicite una búsqueda real y busque en el cuerpo de la respuesta una palabra que espere encontrar. supervisión de palabras clave con Uptime Kuma hace exactamente eso sin herramientas adicionales. Supervise también /stats/errors después de cada actualización de versión, porque los motores cambian su HTML y un analizador puede dejar de funcionar sin que intervenga ningún límite de tasa.
FAQ
¿Por qué SearXNG devuelve 429 a todos los visitantes después de configurarlo detrás de un proxy inverso?
Porque el limitador cuenta el proxy como cliente. SearXNG sólo lee X-Forwarded-For cuando la dirección de conexión aparece en trusted_proxies dentro de /etc/searxng/limiter.toml. Si no aparece, todos los visitantes comparten un contador y superan juntos el límite de 150 solicitudes por 10 minutos. Añada la dirección desde la que se conecta el proxy. En Docker, normalmente es el rango del puente 172.16.0.0/12. Asegúrese también de que el proxy envíe X-Real-IP y X-Forwarded-For. Nunca incluya un rango que no controle, porque una red de confianza permite que cualquier visitante establezca esa cabecera y elija una identidad nueva en cada solicitud.
¿Cuántas solicitudes de API por hora permite el limitador de SearXNG?
Cuatro por dirección IP y por hora. Cualquier solicitud que pida un formato distinto de HTML se contabiliza en una ventana independiente de una hora. Ese límite se establece en searx/botdetection/ip_limit.py, no en limiter.toml, por lo que no se puede aumentar desde la configuración. Un agente o un script lo alcanza en una sola tarea. Añada la dirección del cliente a pass_ip en limiter.toml, o acceda a la instancia mediante una red interna donde el limitador nunca vea la solicitud.
¿Por qué los resultados de búsqueda aparecen vacíos sin un error 429?
Los motores están rechazando al servidor, no a los usuarios. Abra /stats/errors en su propia instancia. Este archivo indica qué motor falló y por qué. Una entrada de CAPTCHA o de acceso denegado significa que ese motor bloqueó la dirección IP del servidor. SearXNG suspende entonces el motor durante una hora después de una respuesta de demasiadas solicitudes y durante un día después de un CAPTCHA. Ningún ajuste local elimina un bloqueo del servicio remoto. Quite los motores que bloquean su dirección y conserve los que responden.
¿Debo activar el limitador en una instancia privada?
Si nadie accede a la instancia salvo usted, deje limiter: false. Añade una dependencia de Valkey y bloquea sus propios scripts, pero protege contra tráfico que no existe. Actívelo en cuanto la instancia tenga una dirección pública, junto con public_instance: true. Esta combinación es intencionada: con public_instance: true y sin un Valkey operativo, el proceso termina con el estado 1 en lugar de ejecutarse sin protección.