SearXNG 429 錯誤與速率限制怎麼修
SearXNG 出現 429 可能是本機 limiter 擋下請求,也可能是搜尋引擎封鎖伺服器 IP。查看 log 辨識原因,再套用正確修法,避免誤改設定。
SearXNG 為何回傳 429 錯誤
自架的 SearXNG 執行個體回傳 429 錯誤,原因可能完全不同,而且通常需要修正的速率限制不是你以為的那一個。第一種原因發生在本機:SearXNG 自己的限制器判定請求來自機器人,並以狀態碼 429 回應 Too Many Requests。第二種原因發生在上游:搜尋引擎拒絕伺服器的 IP 位址,使用者看到的通常是缺少部分內容的結果頁面,而不是 429。
這兩種情況沒有共通的修正方法。限制器由你管理,因此可以修改。上游封鎖發生在 Google 端,所以你在 settings.yml 中進行的任何變更都無法解除封鎖。通常只要查看日誌,大約 1 分鐘內就能判斷是哪一種情況,因此應先從日誌開始。
本指南假設你使用 在自己的 VPS 上自架 SearXNG 執行個體中所述的容器安裝方式。以下所有設定名稱均取自截至 August 2026 的上游文件與原始碼。
變更設定前先查看日誌
開啟日誌視窗後重現問題。
cd ./searxng/
docker compose logs -f searxng-core限制器訊息來自名為 searx.limiter 的 logger,並會指出 IP 位址。命中 blocklist 時會顯示 BLOCK 203.0.113.10: matched BLOCKLIST,命中 allowlist 時則會顯示 PASS 203.0.113.10: matched PASSLIST。如果限制器無法連線至計數器儲存區,日誌會顯示 The limiter requires Valkey, please consult the documentation,這表示目前完全沒有計算任何項目。
每次個別的 bot 檢查都會以 debug 層級記錄,因此預設不會顯示。請在 settings.yml 中開啟 debug,執行一次測試:
general:
debug: true接著,日誌會在用戶端網路旁加入類似 NOT OK (http_accept_language) 的行,並指出失敗的檢查項目。完成後請再次關閉 debug,因為 upstream 不建議讓已部署的 instance 在開啟 debug 的狀態下執行。
Engine 失敗時的訊息完全不同。訊息會指出 engine,而不是 IP 位址。最常見的情況是逾時:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)此外也有一個頁面可供查看。當 enable_metrics 維持預設值 true 時,instance 會在 /stats/errors 記錄 engine 錯誤,而 /preferences 會列出目前有回應的 engine。如果 /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 標籤包含日期和 commit。截至 August 2026,上游 .env.example 中的範例標籤為 2026.3.25-541c6c3cb,因此請在 .env 中設定實際版本:
SEARXNG_VERSION=2026.3.25-541c6c3cb檢查已發布的標籤,並固定你實際測試過的版本,然後針對固定目標進行除錯。同一個 .env 檔案也存放 secret key,因此請先閱讀 Docker Compose 中環境檔案與 secrets 的運作方式,再將該目錄提交到任何位置。
限制器需要 Valkey,否則不會執行
限制器會依用戶端計算請求數量,而且這些計數必須在多個 worker process 之間共享。這個儲存區就是 Valkey,也就是 Redis 的持續維護分支。較舊的 SearXNG 指南會將此設定稱為 redis:。目前版本會讀取 valkey:,因此請從目前的文件複製 key 名稱,不要使用舊文章中的名稱。
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 image 上執行 searxng-valkey service,因此該主機名稱可在 compose network 內解析。也可以透過 SEARXNG_VALKEY_URL environment variable 設定相同值;如果 SearXNG 與 Valkey 共用同一台主機,則 Unix socket URL(unix:///path/to/socket.sock?db=0)也能運作。
儲存區不存在時的行為,取決於另一個 key。使用 public_instance: false 時,限制器會記錄 Valkey 錯誤並放棄,因此該執行個體會繼續提供服務,但完全不執行速率限制。使用 public_instance: true 時,process 會改為呼叫 sys.exit(1),因為 bot protection 損壞的公開執行個體,會在一天內從每個 engine 收集 CAPTCHAs(completely automated public turing test to tell computers and humans apart)。如果你設定 public_instance: true 後,container 立即持續重新啟動,原因就是這個;每次結束前的最後一行會指出 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 實際控制的是用來分組用戶端的位址前綴、受信任 proxy 的清單、可選的連結 token 檢查,以及允許與封鎖清單。
請求會透過標頭檢查被標記為可疑,每項檢查都有一個名稱,您會在 debug log 中看到:
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,或其內容符合已知 bot 模式。http_sec_fetch:Sec-Fetch-Mode或Sec-Fetch-Dest標頭的內容不是瀏覽器會傳送的值。
瀏覽器會傳送上述所有標頭。一般的 curl 呼叫幾乎不會傳送這些標頭,因此手動撰寫的測試請求第一次就會被標記,但相同的搜尋在瀏覽器分頁中卻能正常運作。因此,「在我的瀏覽器中可以運作,但我的 script 收到 429」是正常結果,不是無法解釋的問題。
反向代理後,限流器會同時封鎖所有人
這是讓原本正常的執行個體失效的最常見方式。SearXNG 會從 X-Forwarded-For 中第一個不受信任的 IP 取得用戶端位址,若無法取得,則改用 X-Real-IP,最後再退回到建立連線的位址。是否信任這些標頭,完全由 limiter.toml 中的 trusted_proxies 決定。
如果代理伺服器的位址不在該清單中,這些標頭就會遭到忽略,所有訪客都會以代理伺服器的位址連入。接著他們會共用同一個計數器,因此總請求數超過 10 分鐘內 150 次後,整個網站都會一併遭到封鎖。只要一名使用者重新載入結果頁面幾次,就可能連累所有人。
過度信任的風險更高。如果列入公開網段,任何訪客都能自行傳送 X-Forwarded-For 標頭,為每個請求指定新的身分。知道這種做法的人就能關閉限流。只列出自有代理伺服器連出的位址。在 Docker 中,這通常是 172.16.0.0/12 內的 bridge network,而該行預設會被註解掉。
[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,使用手機的行動數據搜尋一次,並確認日誌行中的 network 是手機的位址,而不是代理伺服器的位址。
代理程式每小時可提出 4 次 API 請求
JSON 輸出預設為停用,因此必須為代理程式啟用:
search:
formats:
- html
- json現在再次查看圖表中的資料列。任何要求 HTML 以外格式的請求,都會在自己的時間窗口中計算:每個位址每 1 hour 可提出 4 次請求。研究代理程式在一項任務中就會用完額度,之後的每次呼叫都會回傳 429。無法提高限制,因為這個數值寫在原始碼中。
正確的修正方式,是告訴 limiter 這個用戶端不是陌生來源。將其位址加入 limiter.toml 中的允許清單:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip 的優先權高於其他所有方法,因此列入允許清單的用戶端也會略過標頭檢查,直接呼叫 curl 即可正常運作。範圍應盡可能縮小,並優先使用 VPN 子網路或容器 network,而不是任何可路由的網段。另一個正確的修正方式,是讓代理程式完全避開公開路徑:將它指向內部 network 上的容器位址,讓 proxy 及其 limiter 完全看不到這些流量。設定方式請參閱讓 AI 代理程式具備 SearXNG 搜尋技能。
應避免的做法,是將代理程式指向其他人執行的公開 instance。這是讓 volunteer 的 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 會引發具名例外,並暫停向該搜尋引擎發出請求。收到 too-many-requests 回應時,會暫停 3600 秒。一般 CAPTCHA 或 access-denied 回應會暫停 1 day。透過 Cloudflare 提供的 CAPTCHA 會暫停 15 days,這是清單中的最長預設時間,因為這表示封鎖位於邊緣端,重試也不會有幫助。
一般失敗使用不同的設定。逾時或解析錯誤會根據 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 中沒有任何設定能改變這項判定。
您可以改變的是要使用哪些搜尋引擎,以及執行個體是否公開列出。供單一家庭使用的私有執行個體很少會觸發封鎖。位於託管服務 IP 上的公開執行個體,會在限制最嚴格的搜尋引擎上不斷遭到暫停;這通常是軟體的正常狀態,不是設定錯誤。SearXNG 可透過 outgoing.proxies 或 outgoing.using_tor_proxy,將搜尋引擎請求經由 proxy 傳送,讓網路流量改用其他位址。Exit node 與低價 proxy pool 的評分通常比託管服務網段更差,因此這項變更可能使結果品質降低。
先監控執行個體,才能第一時間發現問題
即使所有引擎都已暫停,SearXNG 仍會在其連接埠回應。因此,只監控狀態碼的 uptime check 仍會顯示正常,但執行個體實際上不會回傳任何內容。請改為檢查內容:發出實際的搜尋請求,並比對回應本文中預期會出現的字詞。Uptime Kuma 關鍵字監控 不需其他工具即可完成這項工作。每次版本升級後也要監控 /stats/errors,因為引擎可能變更 HTML,導致 parser 在沒有涉及 rate limit 的情況下失效。
FAQ
為什麼將 SearXNG 放在反向代理後方後,所有訪客都收到 429?
因為限制器將代理視為用戶端。只有在連線位址列於 /etc/searxng/limiter.toml 的 trusted_proxies 中時,SearXNG 才會讀取 X-Forwarded-For。若未列入,所有訪客會共用同一個計數器,並一同超過每 10 分鐘 150 個請求的限制。加入代理連線所使用的位址。在 Docker 中,通常是 bridge 範圍 172.16.0.0/12。同時確認代理會傳送 X-Real-IP 與 X-Forwarded-For。切勿列入不受您控制的範圍,因為受信任的網路會讓任何訪客設定該標頭,並為每個請求選擇新的身分。
SearXNG 限制器每小時允許多少個 API 請求?
每個 IP 位址每小時 4 個。任何要求 HTML 以外格式的請求,都會在獨立的一小時計時窗中計算。此限制設定於 searx/botdetection/ip_limit.py,而非 limiter.toml,因此無法從設定檔提高。Agent 或指令碼執行一項工作就會超過此限制。將用戶端位址加入 limiter.toml 中的 pass_ip,或透過內部網路連線至該執行個體,讓限制器不會看到該請求。
為什麼搜尋結果會是空的,卻沒有 429 錯誤?
拒絕您伺服器的是搜尋引擎,而不是使用者。在您自己的執行個體上開啟 /stats/errors。其中會列出每個失敗的搜尋引擎及原因。CAPTCHA 或拒絕存取項目表示該搜尋引擎封鎖了您伺服器的 IP 位址。SearXNG 之後會暫停使用該搜尋引擎:收到過多請求的回應後暫停 1 小時,收到 CAPTCHA 後暫停 1 天。沒有任何本機設定能解除上游封鎖,因此移除會封鎖您位址的搜尋引擎,保留能正常回應的搜尋引擎。
私有執行個體是否應啟用限制器?
如果除了您以外沒有其他來源能連線至該執行個體,請保留 limiter: false。它會增加 Valkey 相依性,也會封鎖您自己的指令碼,而它防護的是不存在的流量。執行個體取得公開位址後,立即啟用它,並一併設定 public_instance: true。這兩項設定是刻意搭配的:啟用 public_instance: true 但 Valkey 無法正常運作時,程序會以狀態 1 結束,而不是在未受保護的情況下執行。