Como corrigir limites de taxa e erros 429 no SearXNG
Erros 429 no SearXNG podem vir do limitador local ou do bloqueio do IP pelos motores. Veja o log, identifique a causa e aplique a correção certa.
Por que o SearXNG retorna erros 429
Uma instância SearXNG auto-hospedada retorna erros 429 por duas razões não relacionadas, e o limite de taxa que precisa de ser corrigido normalmente não é aquele que se presume. A primeira razão é local: o próprio limitador do SearXNG decidiu que um pedido veio de um bot e respondeu Too Many Requests com o estado 429. A segunda é upstream: um motor de pesquisa recusou o endereço IP do seu servidor, o que chega aos seus utilizadores como uma página de resultados com elementos em falta, e não como um erro 429.
Os dois casos não têm a mesma correção. O limitador é seu, portanto pode alterá-lo. O bloqueio upstream ocorre no lado do Google, portanto nada na sua settings.yml o irá remover. O log informa qual dos casos está a ocorrer em cerca de um minuto, por isso comece por aí.
Este guia pressupõe a instalação em contentor descrita em uma instância SearXNG auto-hospedada no seu próprio VPS. Todos os nomes de definições abaixo vêm da documentação e do código-fonte upstream atuais, verificados em agosto de 2026.
Leia o log antes de alterar uma configuração
Reproduza o problema com uma janela de log aberta.
cd ./searxng/
docker compose logs -f searxng-coreAs mensagens do limiter vêm do logger chamado searx.limiter e identificam um endereço IP. Uma ocorrência na blocklist aparece como BLOCK 203.0.113.10: matched BLOCKLIST, e uma ocorrência na allowlist aparece como PASS 203.0.113.10: matched PASSLIST. Se o limiter não conseguir alcançar o armazenamento dos contadores, o log indica The limiter requires Valkey, please consult the documentation. Isso significa que nada está a ser contabilizado.
Cada verificação individual de bot é registada no nível debug, por isso não a verá por predefinição. Ative o debug para um teste em settings.yml:
general:
debug: trueO log passa então a incluir linhas no formato NOT OK (http_accept_language) junto da rede do cliente, identificando a verificação que falhou. Desative-o novamente depois, porque o upstream recomenda não executar uma instância em produção com o debug ativado.
As falhas do engine têm um formato completamente diferente. Identificam um engine em vez de um IP, e a mais comum é um timeout:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)Também existe uma página para isto. Com enable_metrics no valor predefinido de true, a sua instância regista os erros dos engines em /stats/errors, e /preferences lista os engines que estão atualmente a responder. Se /stats/errors estiver cheio e o log não contiver linhas searx.limiter, o limiter não é a causa do problema.
Fixe a versão antes de depurar qualquer coisa
A configuração do container upstream é composta por dois ficheiros.
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 .envO ficheiro Compose descarrega docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. Uma variável não definida significa latest, e latest significa que a instância muda no docker compose pull seguinte. Assim, uma definição que funcionou na semana passada pode deixar de corresponder ao código que a lê. As tags do SearXNG incluem uma data e um commit. A tag de exemplo no .env.example upstream em agosto de 2026 é 2026.3.25-541c6c3cb. Defina uma versão real em .env:
SEARXNG_VERSION=2026.3.25-541c6c3cbConsulte as tags publicadas e fixe a release que testou efetivamente. Depois, faça a depuração com um alvo fixo. O mesmo ficheiro .env contém a sua chave secreta. Leia como funcionam os ficheiros env e os segredos no Docker Compose antes de fazer commit desse diretório em qualquer local.
O limitador precisa do Valkey para funcionar
O limitador conta os pedidos por cliente, e essas contagens têm de ser partilhadas entre os processos de trabalho. Esse armazenamento é o Valkey, o fork mantido do Redis. Os guias antigos do SearXNG chamam a esta definição redis:. As versões atuais leem valkey:, por isso copie o nome da chave da documentação atual, e não de uma publicação antiga.
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0O ficheiro compose do projeto já inicia um serviço searxng-valkey com a imagem docker.io/valkey/valkey:9-alpine, por isso esse nome de host é resolvido dentro da rede do compose. O mesmo valor pode ser definido com a variável de ambiente SEARXNG_VALKEY_URL, e um URL de socket Unix (unix:///path/to/socket.sock?db=0) funciona quando o SearXNG e o Valkey partilham o mesmo host.
O comportamento quando o armazenamento está indisponível depende de outra chave. Com public_instance: false, o limitador regista o erro do Valkey e desiste, por isso a instância continua a servir pedidos sem qualquer limitação de taxa. Com public_instance: true, o processo chama sys.exit(1), porque uma instância pública com a proteção contra bots avariada recolhe CAPTCHAs (teste público de Turing completamente automatizado para distinguir computadores de pessoas) de todos os motores no prazo de um dia. Se um contentor entrar num ciclo de reinícios logo depois de definir public_instance: true, a causa é esta, e a última linha antes de cada saída identifica o Valkey.
O que o limitador realmente contabiliza
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"
}
]Um cliente normal pode fazer 15 pedidos dentro de uma janela de rajada de 20 segundos e 150 dentro de uma janela de 10 minutos. Quando um pedido é marcado como suspeito, o mesmo cliente passa a ter apenas 2 por janela de rajada. A última linha é a mais restritiva: depois de 3 pedidos marcados dentro de uma janela de 30 dias, esse endereço é redirecionado para a página inicial em vez de pesquisar, e o log indica BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).
Estes números são constantes em searx/botdetection/ip_limit.py. Não são definições de configuração, e limiter.toml não os expõe. Para os alterar, é necessário editar o código-fonte. O que /etc/searxng/limiter.toml controla são os prefixos de endereço usados para agrupar clientes, a lista de proxies confiáveis, a verificação opcional do token de ligação e as listas de permissão e bloqueio.
Um pedido é marcado como suspeito pelas verificações dos cabeçalhos. Cada verificação tem um nome que aparecerá no log de depuração:
http_accept: o cabeçalhoAcceptnão contémtext/html.http_accept_encoding: o cabeçalhoAccept-Encodingnão identificagzipnemdeflate.http_accept_language: não existe nenhum cabeçalhoAccept-Language.http_connection: o cabeçalhoConnectionestá definido comoclose.http_user_agent:User-Agentestá ausente ou corresponde a um padrão de bot conhecido.http_sec_fetch: o cabeçalhoSec-Fetch-ModeouSec-Fetch-Destnão corresponde ao que um navegador envia.
Um navegador envia todos estes elementos. Uma chamada simples de curl envia quase nenhum deles. Por isso, um pedido de teste escrito manualmente é marcado logo na primeira tentativa, enquanto a mesma pesquisa funciona num separador do navegador. É por isso que “funciona no meu navegador, mas o meu script recebe 429” é o resultado normal, e não um mistério.
O limitador bloqueia todos ao mesmo tempo atrás de um reverse proxy
Esta é a forma mais comum de deixar uma instância funcional indisponível. O SearXNG obtém o endereço do cliente a partir do primeiro IP não confiável em X-Forwarded-For, recorre a X-Real-IP e, se necessário, usa novamente o endereço que abriu a ligação. A decisão de confiar ou não nesses cabeçalhos é controlada por trusted_proxies em limiter.toml.
Se o endereço do proxy não estiver nessa lista, os cabeçalhos são ignorados e todos os visitantes chegam com o endereço do proxy. Assim, partilham um único contador e todo o site é bloqueado quando o total ultrapassa 150 pedidos em 10 minutos. Um utilizador que recarregue uma página de resultados algumas vezes pode bloquear o acesso de todos.
Confiar em excesso é pior. Se for listado um intervalo público, qualquer visitante pode enviar o seu próprio cabeçalho X-Forwarded-For e escolher uma identidade nova para cada pedido. Isto desativa o limitador para quem souber explorar o comportamento. Liste apenas o endereço a partir do qual o seu próprio proxy estabelece ligações. No Docker, normalmente esse endereço pertence a uma bridge network dentro de 172.16.0.0/12, e essa linha vem comentada.
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]O proxy também tem de enviar os cabeçalhos. O Nginx não adiciona nenhum deles por si só:
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;
}O Caddy e o Traefik definem os cabeçalhos encaminhados automaticamente. Com essas ferramentas, só precisa de configurar a parte trusted_proxies. As vantagens e desvantagens são apresentadas em escolher um reverse proxy para um serviço self-hosted. Para verificar qualquer uma das configurações, ative debug, faça uma pesquisa a partir do telemóvel usando dados móveis e confirme no registo que a rede corresponde ao endereço do telemóvel, e não ao do proxy.
Seu agente recebe quatro solicitações de API por hora
A saída JSON está desativada por padrão, portanto é necessário adicioná-la ao agente:
search:
formats:
- html
- jsonAgora leia novamente a linha da tabela. Qualquer solicitação que peça um formato diferente de HTML é contabilizada na sua própria janela: 4 solicitações por 1 hour, por endereço. Um agente de pesquisa esgota esse limite numa tarefa, e todas as chamadas seguintes retornam 429. Aumentar o limite não é uma opção, porque o valor está definido no código-fonte.
A correção adequada é informar ao limitador que este cliente não é desconhecido. Adicione o endereço à lista de permissões em limiter.toml:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip tem prioridade sobre todos os outros métodos, portanto um cliente na lista de permissões também ignora as verificações de cabeçalhos e uma chamada simples para curl funciona. Mantenha o intervalo tão pequeno quanto possível e prefira uma sub-rede VPN ou uma rede de contentores a qualquer rede encaminhável. A outra correção adequada é manter o agente completamente fora do caminho público: aponte-o para o endereço do contentor na rede interna, onde o proxy e o respetivo limitador nunca veem o tráfego. A configuração disso é explicada em dar a um agente de IA uma competência de pesquisa no SearXNG.
Evite apontar o agente para uma instância pública mantida por outra pessoa. Essa é a forma mais rápida de fazer com que o endereço IP de um voluntário seja bloqueado pelos motores upstream, e é precisamente por isso que o formato JSON está desativado por padrão.
Quando os mecanismos bloqueiam você
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"
}
]Quando um mecanismo responde com o próprio erro 429 ou com uma página CAPTCHA, o SearXNG gera uma exceção identificada e deixa de consultar esse mecanismo por algum tempo. Uma resposta de excesso de pedidos suspende o mecanismo por 3600 segundos. Uma resposta simples de CAPTCHA ou de acesso negado suspende-o por 1 day. Um CAPTCHA servido pelo Cloudflare suspende-o por 15 days, o período padrão mais longo da lista, porque essa resposta indica que o bloqueio está na borda da rede e que novas tentativas não ajudarão.
As falhas comuns usam configurações diferentes. Um timeout ou erro de análise suspende o mecanismo por um período curto derivado de search.ban_time_on_fail, que usa 5 segundos por padrão e é limitado por search.max_ban_time_on_fail a 120 segundos. Assim, um mecanismo lento recupera-se sozinho em poucos minutos, enquanto um mecanismo bloqueado fica indisponível durante horas. Essa diferença explica um sintoma que costuma ser descrito como aleatório: os resultados estão normais e, depois, os resultados de um mecanismo desaparecem durante o resto da tarde.
Vale a pena corrigir os timeouts antes de atribuir a culpa a alguém. O valor padrão de request_timeout é 2.0 segundos, o que é pouco para um VPS pequeno localizado longe do servidor de borda mais próximo do mecanismo.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout é o valor padrão para todos os mecanismos, max_request_timeout é o limite máximo e um mecanismo individual pode ter o seu próprio timeout. Aumentar esses valores troca latência da página por menos falhas. Por isso, aumente em incrementos de meio segundo e monitorize /stats/errors, em vez de saltar diretamente para 10.
Se um mecanismo estiver realmente a bloquear o seu endereço, remova-o. Cada pesquisa espera pelo mecanismo mais lento. Manter um mecanismo permanentemente suspenso aumenta a latência e não devolve resultados.
use_default_settings:
engines:
remove:
- googleAplique as alterações com docker compose restart searxng-core, faça algumas pesquisas e recarregue /stats/errors. Uma página vazia após cinco minutos de utilização real indica que a alteração funcionou.
Um IP de um datacenter será tratado como um bot
O endereço do seu VPS pertence a uma faixa de alojamento, e os principais motores de pesquisa classificam essas faixas como automação. Alguns apresentam um CAPTCHA em todos os pedidos provenientes desse endereço, independentemente de os cabeçalhos serem adequados ou do ritmo ser lento. Nenhuma definição em settings.yml altera essa avaliação.
O que pode alterar é quais motores consulta e se a sua instância está listada publicamente. Uma instância privada usada por uma única família raramente aciona esses mecanismos. Uma instância pública num IP de alojamento acumulará suspensões nos motores mais rigorosos, e esse é o comportamento normal do software, não uma falha na sua configuração. O SearXNG pode encaminhar os pedidos aos motores através de um proxy com outgoing.proxies ou outgoing.using_tor_proxy, transferindo o tráfego para um endereço diferente. Os nós de saída e os pools de proxies baratos têm uma classificação pior do que as faixas de alojamento, por isso essa alteração pode piorar os resultados.
Monitore a instância para detetar problemas primeiro
O SearXNG responde na sua porta mesmo quando todos os motores estão suspensos. Por isso, uma verificação de disponibilidade que analisa apenas o código de estado continua verde enquanto a instância não devolve resultados. Verifique o conteúdo. Faça uma pesquisa real e procure no corpo da resposta uma palavra que espera encontrar. monitorização de palavras-chave do Uptime Kuma faz exatamente isso sem ferramentas adicionais. Monitorize também /stats/errors após cada atualização de versão, porque os motores alteram o respetivo HTML e um parser pode deixar de funcionar sem qualquer relação com limites de taxa.
FAQ
Por que o SearXNG retorna 429 para todos os visitantes depois de ser colocado atrás de um reverse proxy?
Porque o limitador está contando o proxy como cliente. O SearXNG só lê X-Forwarded-For quando o endereço de ligação está listado em trusted_proxies em /etc/searxng/limiter.toml. Se não estiver listado, todos os visitantes partilham o mesmo contador e ultrapassam juntos o limite de 150 pedidos por 10 minutos. Adicione o endereço a partir do qual o proxy estabelece a ligação. No Docker, normalmente é o intervalo da bridge 172.16.0.0/12. Confirme também se o proxy envia X-Real-IP e X-Forwarded-For. Nunca liste um intervalo que não controle, porque uma rede confiável permite que qualquer visitante defina esse cabeçalho e escolha uma nova identidade em cada pedido.
Quantos pedidos de API por hora o limitador do SearXNG permite?
Quatro por endereço IP por hora. Qualquer pedido que solicite um formato diferente de HTML é contabilizado numa janela separada de 1 hora. Esse limite é definido em searx/botdetection/ip_limit.py, e não em limiter.toml, portanto não pode ser aumentado na configuração. Um agente ou script esgota esse limite numa única tarefa. Adicione o endereço do cliente a pass_ip em limiter.toml ou aceda à instância através de uma rede interna onde o limitador nunca veja o pedido.
Por que os meus resultados de pesquisa ficam vazios sem um erro 429?
Os motores estão a recusar o seu servidor, não os seus utilizadores. Abra /stats/errors na sua própria instância. Esse ficheiro identifica cada motor que falhou e explica o motivo. Uma entrada de CAPTCHA ou de acesso negado significa que esse motor bloqueou o endereço IP do seu servidor. O SearXNG suspende então o motor durante 1 hora depois de uma resposta de demasiados pedidos e durante 1 dia depois de um CAPTCHA. Nenhuma definição local remove um bloqueio aplicado pelo serviço remoto. Remova os motores que bloqueiam o seu endereço e mantenha os que respondem.
Devo ativar o limitador numa instância privada?
Se nada além de si aceder à instância, deixe limiter: false desativado. Ele adiciona uma dependência do Valkey, bloqueia os seus próprios scripts e protege contra tráfego que não existe. Ative-o assim que a instância tiver um endereço público, juntamente com public_instance: true. Essa combinação é intencional: com public_instance: true e sem um Valkey funcional, o processo termina com o estado 1 em vez de funcionar sem proteção.