SearXNG 429 错误与速率限制排查修复
SearXNG 出现 429 可能是本地限制器拦截,也可能是上游搜索引擎封锁服务器 IP。查看日志中的 limiter、IP 和引擎名称,约 1 分钟内确定原因并采取对应修复。
SearXNG 返回 429 错误的原因
自托管 SearXNG 实例返回 429 错误通常有两个互不相关的原因,而需要修复的速率限制通常不是您以为的那一个。第一个原因发生在本地:SearXNG 自己的限制器判断请求来自机器人,并以状态码 429 返回 Too Many Requests。第二个原因发生在上游:搜索引擎拒绝了您服务器的 IP 地址,用户看到的通常是缺少部分内容的结果页面,而不是 429 错误。
这两种情况没有共同的修复方法。限制器由您控制,因此可以修改。上游封锁发生在 Google 一侧,因此修改您的 settings.yml 无法解除封锁。日志通常能在约 1 分钟内告诉您属于哪种情况,因此应从日志开始排查。
本指南假定您使用的是 在自己的 VPS 上运行自托管 SearXNG 实例 中介绍的容器安装方式。以下所有设置名称均来自截至 2026 年 8 月核对过的当前上游文档和源代码。
更改设置前先查看日志
打开日志窗口,重现问题。
cd ./searxng/
docker compose logs -f searxng-core限制器消息来自名为 searx.limiter 的日志记录器,并包含 IP 地址。命中阻止列表时,日志内容为 BLOCK 203.0.113.10: matched BLOCKLIST;命中允许列表时,日志内容为 PASS 203.0.113.10: matched PASSLIST。如果限制器无法访问计数器存储,日志会显示 The limiter requires Valkey, please consult the documentation,这表示根本没有进行计数。
每次单独的机器人检查都会以 debug 级别记录,因此默认情况下看不到这些日志。在 settings.yml 中开启 debug,执行一次测试:
general:
debug: true随后,日志会在客户端网络旁添加类似 NOT OK (http_accept_language) 的行,并指出失败的检查。测试完成后立即关闭 debug,因为上游明确要求已部署实例不得在启用 debug 的情况下运行。
引擎故障的表现完全不同。日志会记录引擎名称,而不是 IP 地址。最常见的故障是超时:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)此问题也有对应的页面。将 enable_metrics 保持为默认值 true 时,实例会在 /stats/errors 记录引擎错误;/preferences 会列出当前正在响应的引擎。如果 /stats/errors 已满,而日志中没有 searx.limiter 行,则问题不在限制器。
在开始排查任何问题前固定版本
上游容器配置由两个文件组成。
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 .envCompose 文件会拉取 docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}。未设置变量意味着 latest,而 latest 表示实例会在下一次 docker compose pull 时发生变化。因此,上周正常工作的设置可能不再匹配读取它的代码。SearXNG 标签包含日期和提交。以 2026 年 8 月为准,上游 .env.example 中的示例标签是 2026.3.25-541c6c3cb,因此请在 .env 中设置一个实际版本:
SEARXNG_VERSION=2026.3.25-541c6c3cb检查已发布的标签,并固定到经过实际测试的版本,然后针对固定目标进行排查。同一个 .env 文件还包含您的密钥,因此在将该目录提交到任何位置前,请先阅读 Docker Compose 中环境文件和密钥的工作方式。
限流器需要 Valkey,否则无法运行
限流器按客户端统计请求次数,这些计数必须在多个工作进程之间共享。该存储使用 Valkey,它是 Redis 的维护分支。较旧的 SearXNG 指南将此设置称为 redis:。当前版本读取 valkey:,因此应从当前文档复制键名,不要从旧文章中复制。部分页面的内容更早,介绍的是 Searx,而不是 SearXNG;两者是不同的代码库,使用的限流器也不同。因此,在从页面复制配置块之前,先确认页面针对的是哪一个项目。
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0上游 compose 文件已经在 docker.io/valkey/valkey:9-alpine 镜像上运行 searxng-valkey 服务,因此该主机名可在 compose 网络内解析。同样的值也可以通过 SEARXNG_VALKEY_URL 环境变量设置。当 SearXNG 和 Valkey 位于同一台主机时,可以使用 Unix socket URL(unix:///path/to/socket.sock?db=0)。
存储服务缺失时的行为取决于另一个键。设置为 public_instance: false 时,限流器会记录 Valkey 错误并放弃运行,因此实例会继续提供服务,但完全不执行限流。设置为 public_instance: true 时,进程会改为调用 sys.exit(1),因为开放且机器人防护失效的实例会在一天内从每个搜索引擎收集 CAPTCHA(全自动区分计算机和人类的公开图灵测试)。如果设置 public_instance: true 后容器立即进入循环重启,这就是该情况;每次退出前的最后一行日志会指出 Valkey。
限流器实际统计的内容
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"
}
]普通客户端在 20 秒突发窗口内可发送 15 个请求,在 10 分钟窗口内可发送 150 个请求。一旦某个请求被标记为可疑,同一客户端在每个突发窗口内只能发送 2 个请求。最后一行的限制最严格:如果某个地址在 30 天窗口内出现 3 个被标记的请求,该地址会被重定向到起始页,而不是执行搜索;日志会记录 BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /)。
这些数字是 searx/botdetection/ip_limit.py 中的常量。它们不是配置项,limiter.toml 也不会公开这些值,因此要修改它们必须编辑源代码。/etc/searxng/limiter.toml 实际控制的是用于对客户端分组的地址前缀、受信任代理列表、可选的链接令牌检查,以及允许列表和阻止列表。
请求会根据请求头检查结果被标记为可疑,每项检查都有一个名称,您会在调试日志中看到这些名称:
http_accept:Accept请求头不包含text/html。http_accept_encoding:Accept-Encoding请求头既未列出gzip,也未列出deflate。http_accept_language:不存在Accept-Language请求头。http_connection:Connection请求头被设置为close。http_user_agent:User-Agent缺失,或匹配已知的机器人模式。http_sec_fetch:Sec-Fetch-Mode或Sec-Fetch-Dest请求头的内容不是浏览器发送的内容。
浏览器会发送上述所有请求头。普通的 curl 调用几乎不会发送其中任何一个,因此手动编写的测试请求第一次尝试就会被标记,而相同的搜索在浏览器标签页中却可以正常工作。这就是“在我的浏览器中可以运行,但我的脚本收到 429”成为正常结果的原因,而不是什么难以解释的问题。
反向代理后限流器会同时阻断所有用户
这是导致正常运行的实例失效的最常见原因。SearXNG 会从 X-Forwarded-For 中第一个不受信任的 IP 获取客户端地址;如果没有,则回退到 X-Real-IP;如果仍没有,则使用建立连接的地址。是否信任这些请求头,由 limiter.toml 中的 trusted_proxies 决定。
如果代理地址不在该列表中,系统会忽略这些请求头,每个访客都会以代理地址访问。这样所有访客共用一个计数器;当总数在 10 分钟内超过 150 次请求后,整个站点会一起被阻断。一个用户反复刷新几次结果页,就可能导致所有人都无法访问。
过度信任的风险更大。如果列表中包含公网地址段,任何访客都可以发送自己的 X-Forwarded-For 请求头,并为每个请求选择新的身份。这样一来,知道该方法的用户就可以绕过限流。这里只应列出您自己的代理连接所使用的地址。在 Docker 中,这通常是 172.16.0.0/12 内的桥接网络,而该配置行默认处于注释状态。
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]代理也必须发送这些请求头。Nginx 不会自动添加其中任何一个:
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 和 Traefik 会自动设置转发请求头,因此使用它们时,您只需完成 trusted_proxies 这一部分。相关取舍请参阅为自托管服务选择反向代理。要验证任一配置,请启用 debug,使用手机移动数据搜索一次,并确认日志行中的网络地址是手机地址,而不是代理地址。
您的代理每小时获得 4 次 API 请求
JSON 输出默认处于禁用状态,因此需要为代理启用该选项:
search:
formats:
- html
- json现在再次查看图表中的这一行。任何请求非 HTML 格式的请求都会在独立窗口中计数:每个地址在每个 1 hour 内最多 4 次请求。研究代理在一个任务中就会耗尽该额度,之后的每次调用都会返回 429。无法通过提高限制解决,因为该数值写在源代码中。
正确的解决方法是告知限制器:此客户端不是未知客户端。将其地址添加到 limiter.toml 中的放行列表:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip 的优先级高于其他所有方法,因此加入放行列表的客户端也会跳过请求头检查,直接调用 curl 即可正常工作。将范围限制在尽可能小的范围内,并优先使用 VPN 子网或容器网络,不要使用任何可路由的网络。另一个正确的解决方法是让代理完全避开公网路径:将其指向内部网络中的容器地址,这样代理及其限制器就不会看到这些流量。为 AI 代理配置 SearXNG 搜索技能介绍了具体配置方法。
应避免将代理指向他人运行的公共实例。这样最容易导致志愿者的 IP 地址被上游搜索引擎封禁;这也是 JSON 格式默认禁用的根本原因。
当引擎阻止您时
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"
}
]当引擎返回自身的 429 响应或 CAPTCHA 页面时,SearXNG 会抛出命名异常,并在一段时间内停止向该引擎发送请求。429(请求过多)响应会将其暂停 3600 秒。普通 CAPTCHA 或访问被拒绝的响应会将其暂停 1 day。通过 Cloudflare 提供的 CAPTCHA 会将其暂停 15 days,这是列表中默认时长最长的一项,因为这表示拦截发生在边缘层,重试不会有帮助。您命中的 3 个 CAPTCHA 行中的哪一行,会决定接下来哪些尝试值得进行;确定实例记录的是哪种异常后,CAPTCHA 错误有各自的修复方法。
普通故障使用不同的设置。超时或解析错误会根据 search.ban_time_on_fail 计算出较短的暂停时间。该值默认为 5 秒,并由 search.max_ban_time_on_fail 限制为最多 120 秒。因此,响应缓慢的引擎会在几分钟内自动恢复,而被阻止的引擎会停用数小时。这解释了一个常被认为是随机的问题:开始时结果正常,随后某个引擎的结果在整个下午都消失。
在归咎于其他原因之前,应先处理超时问题。默认的 request_timeout 为 2.0 秒。对于位于远离引擎最近边缘服务器的小型 VPS,这个值可能过短。
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout 是所有引擎的默认值,max_request_timeout 是上限,单个引擎也可以设置自己的 timeout。提高这些值可以减少故障,但会增加页面延迟。因此,应以半秒为步长逐步调整,并监控 /stats/errors,不要直接跳到 10。
如果某个引擎确实阻止了您的地址,请将其移除。每次搜索都要等待最慢的引擎,因此保留一个永久暂停的引擎只会增加延迟,且不会返回结果。
use_default_settings:
engines:
remove:
- google使用 docker compose restart searxng-core 应用更改,然后执行几次搜索并重新加载 /stats/errors。实际使用 5 分钟后页面为空,表示更改已生效。
数据中心 IP 会被视为机器人
您的 VPS 地址属于托管服务地址段,大型搜索引擎通常会将这些地址段判定为自动化流量。无论请求头设置得多规范、请求频率多低,其中一些引擎都会对来自此类地址的每个请求显示 CAPTCHA。settings.yml 中的任何设置都无法改变这一判定。搜索引擎看到的是您的服务器,而不是输入查询的用户;这也是您选择自行托管后需要承担的全部隐私权衡。在假定 SearXNG 能提供更多隐私保护之前,建议先阅读SearXNG 实际隐藏了多少信息。
您可以改变要请求哪些引擎,以及实例是否公开列出。供一个家庭使用的私有实例很少会触发限制。运行在托管服务 IP 上的公共实例会被最严格的引擎陆续暂停访问。这是软件的正常运行状态,不是配置故障。SearXNG 可以通过 outgoing.proxies 或 outgoing.using_tor_proxy 将引擎请求经由代理转发,从而让流量使用其他地址。出口节点和廉价代理池的评分通常比托管服务地址段更差,因此这种做法可能导致搜索结果质量下降。
先监控实例,及时发现问题
即使所有引擎都处于暂停状态,SearXNG 仍会在其端口上响应。因此,只监控状态码的运行时间检查会保持正常,但实例实际上不会返回任何内容。应检查响应内容:发起一次真实搜索,并匹配响应正文中预期出现的词语。Uptime Kuma 关键词监控无需额外工具即可完成此操作。每次版本升级后也要检查 /stats/errors,因为引擎会更改 HTML,解析器可能因此失效,而且这与速率限制无关。
FAQ
为什么将 SearXNG 放在反向代理后面后,所有访问者都会收到 429?
因为限流器将代理视为客户端。只有当连接地址列在 /etc/searxng/limiter.toml 的 trusted_proxies 中时,SearXNG 才会读取 X-Forwarded-For。如果未列出该地址,所有访问者共用一个计数器,并会一起超过每 10 分钟 150 个请求的限制。添加代理连接所使用的地址。在 Docker 中,这通常是网桥范围 172.16.0.0/12。同时确保代理发送 X-Real-IP 和 X-Forwarded-For。不要列出不受您控制的网络范围,因为受信任网络中的任何访问者都可以设置该请求头,并为每个请求选择新的身份。
SearXNG 限流器每小时允许多少个 API 请求?
每个 IP 地址每小时 4 个。请求 HTML 以外格式的请求会单独计入一个小时窗口。该限制在 searx/botdetection/ip_limit.py 中设置,而不是在 limiter.toml 中设置,因此无法通过配置提高。代理或脚本在一个任务中就会达到该限制。将客户端地址添加到 limiter.toml 中的 pass_ip,或者通过内部网络访问该实例,这样限流器就不会看到该请求。
为什么搜索结果为空,却没有 429 错误?
拒绝您服务器的是搜索引擎,而不是用户。在您自己的实例上打开 /stats/errors。其中会列出失败的每个搜索引擎及失败原因。出现 CAPTCHA 或拒绝访问记录,表示该搜索引擎阻止了您服务器的 IP 地址。随后,SearXNG 会暂停该搜索引擎:收到请求过多的响应后暂停 1 小时,收到 CAPTCHA 后暂停 1 天。没有本地设置可以解除上游阻止。因此,请移除阻止您地址的搜索引擎,只保留能够正常响应的搜索引擎。
私有实例是否应启用限流器?
如果除了您之外没有其他访问者,请保留 limiter: false。它会增加 Valkey 依赖,也会阻止您自己的脚本,并且无法防御您不会收到的流量。实例获得公网地址后,应立即启用它,同时启用 public_instance: true。这两个设置必须同时使用:启用 public_instance: true 但没有正常工作的 Valkey 时,进程会以状态码 1 退出,而不是在未受保护的情况下运行。