SearXNGの429エラーとレート制限を直す方法
SearXNGの429エラーは、自分のレート制限か検索エンジンによるサーバーIPのブロックが原因です。ログで約1分で判別し、原因に合った対処法を確認できます。
SearXNG が 429 エラーを返す理由
自己ホストの SearXNG インスタンスが 429 エラーを返す原因は、互いに無関係な 2 つです。通常、修正が必要なレート制限は想定しているものとは異なります。1 つ目の原因はローカル側です。SearXNG 自身の制限機能がリクエストをボットからのものと判断し、Too Many Requests にステータス 429 を返します。2 つ目はアップストリーム側です。検索エンジンがサーバーの IP アドレスからのアクセスを拒否すると、ユーザーには 429 ではなく、結果が欠落した検索結果ページとして表示されます。
この 2 つのケースに共通する解決策はありません。制限機能は自分の環境にあるため、変更できます。アップストリームによるブロックは 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 と記録されます。これは、カウントがまったく行われていないことを意味します。
個々の bot チェックは debug レベルで記録されるため、デフォルトでは表示されません。settings.yml で debug を 1 回のテストだけ有効にします。
general:
debug: trueその後、ログにはクライアントのネットワーク情報の近くに NOT OK (http_accept_language) 形式の行が追加され、失敗したチェックが示されます。テスト後は debug を再び無効にしてください。upstream も、debug を有効にしたままデプロイ済みのインスタンスを実行しないように案内しています。
エンジンの障害は、これらとはまったく異なる形式で記録されます。IP アドレスではなくエンジン名が示されます。最も一般的な障害はタイムアウトです。
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)この情報を確認できるページもあります。enable_metrics がデフォルト値の true のままの場合、インスタンスはエンジンエラーを /stats/errors に記録し、/preferences には現在応答しているエンジンが一覧表示されます。/stats/errors が満杯で、ログに searx.limiter の行がない場合、問題は制限機能にありません。
デバッグを始める前にバージョンを固定する
上流のコンテナ構成は2つのファイルで成り立っています。
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 ファイルには Secret key も保存するため、そのディレクトリをどこかにコミットする前に、Docker Compose で env ファイルと Secret がどのように機能するかを確認してください。
リミッターには Valkey が必要です。Valkey がなければ動作しません
リミッターはクライアントごとにリクエスト数を数えます。このカウントはワーカープロセス間で共有する必要があります。その保存先が Valkey です。Valkey は Redis のメンテナンス版フォークです。古い SearXNG ガイドでは、この設定を redis: と記載しています。現在のリリースでは valkey: を読み込むため、古い投稿ではなく、現行ドキュメントからキー名をコピーしてください。
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 ソケット URL(unix:///path/to/socket.sock?db=0)も使用できます。
ストアがない場合の動作は、もう 1 つのキーによって異なります。public_instance: false の場合、リミッターは Valkey のエラーをログに記録して処理を中止します。そのため、インスタンスはレート制限なしでサービスを提供し続けます。public_instance: true の場合、プロセスは代わりに sys.exit(1) を呼び出します。ボット対策が壊れたまま公開されたインスタンスでは、1 日以内にすべての検索エンジンから CAPTCHA(completely automated public turing test to tell computers and humans apart)を収集してしまうためです。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 リクエストを超えると、サイト全体がまとめてブロックされます。1 人のユーザーが検索結果ページを数回再読み込みするだけで、全員が巻き込まれます。
信頼しすぎると、さらに危険です。公開範囲のアドレスを登録すると、訪問者は誰でも独自の 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 を有効にし、モバイルデータ通信中のスマートフォンから 1 回検索してください。ログ行の network がプロキシのアドレスではなく、スマートフォンのアドレスになっていることを確認します。
エージェントが 1 時間に実行できる API リクエストは 4 件です
JSON 出力はデフォルトで無効になっているため、エージェント側で有効にする必要があります。
search:
formats:
- html
- json次に、チャートの行をもう一度確認します。HTML 以外の形式を要求するリクエストは、形式ごとに独自のウィンドウでカウントされます。アドレスごとに 4 件のリクエストを 1 hour ごとに実行できます。リサーチエージェントは 1 回のタスクでこの上限に達し、その後のすべての呼び出しは 429 を返します。上限を引き上げることはできません。上限値がソースコードに記述されているためです。
確実な解決策は、このクライアントを未知のクライアントとして扱わないよう limiter に伝えることです。limiter.toml の pass list にアドレスを追加します。
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip は他のすべての方法より優先されます。そのため、allowlist に登録したクライアントではヘッダーチェックも省略され、単純な curl 呼び出しが機能します。範囲は可能な限り小さくしてください。ルーティング可能なネットワークより、VPN のサブネットまたはコンテナネットワークを優先します。もう 1 つの確実な解決策は、エージェントをパブリック経路から完全に外すことです。内部ネットワーク上のコンテナアドレスを指定すれば、プロキシとその limiter にトラフィックが到達しません。この接続方法については、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 は名前付き例外を発生させ、そのエンジンへのリクエストをしばらく停止します。Too Many Requests 応答の場合は、3600 秒間停止します。通常の CAPTCHA またはアクセス拒否の応答の場合は、1 day の間停止します。Cloudflare 経由で提供された CAPTCHA の場合は、15 days の間停止します。これはリスト内で最も長いデフォルト値です。この応答はブロックがエッジ側にあることを示しており、再試行しても解決しないためです。
通常の障害には別の設定が使われます。タイムアウトまたは解析エラーが発生すると、エンジンは search.ban_time_on_fail から算出された短い時間だけ停止します。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 を設定できます。これらの値を上げると、ページの待ち時間が増える代わりに失敗が減ります。そのため、一度に 10 まで上げず、0.5 秒ずつ変更して /stats/errors を確認してください。
アドレスを実際にブロックしているエンジンは削除してください。検索は最も遅いエンジンを待つため、恒久的に停止されたエンジンを残すと待ち時間だけが増え、結果は返りません。
use_default_settings:
engines:
remove:
- googledocker compose restart searxng-core で変更を適用し、検索を数回実行してから /stats/errors を再読み込みします。実際に 5 分間使用した後もページが空であれば、変更は適用されています。
データセンターの IP はボットとして扱われます
VPS のアドレスはホスティング事業者のレンジに属しており、大手検索エンジンはそのレンジを自動化されたアクセスとして評価します。検索エンジンによっては、そのようなアドレスからのすべてのリクエストに対して、ヘッダーが適切でアクセス間隔が長くても CAPTCHA を表示します。settings.yml の設定では、この判定は変更できません。
変更できるのは、問い合わせる検索エンジンと、インスタンスを公開一覧に載せるかどうかです。1 世帯だけが使用する非公開インスタンスでは、問題が発生することはほとんどありません。ホスティング事業者の IP で公開インスタンスを運用すると、制限の厳しい検索エンジンでは停止措置が蓄積します。これは設定ミスではなく、ソフトウェアの通常の動作です。SearXNG では outgoing.proxies または outgoing.using_tor_proxy を使用して検索エンジンへのリクエストをプロキシ経由にし、別のアドレスからネットワークトラフィックを送信できます。ただし、出口ノードや安価なプロキシプールは、ホスティング事業者のレンジより低く評価されます。そのため、切り替えによって結果が悪化する可能性があります。
インスタンスを監視し、最初に異常を検知できるようにします
すべてのエンジンが一時停止していても、SearXNG はポート上で応答します。そのため、ステータスコードだけを監視する稼働時間チェックでは、インスタンスが何も返していなくても正常な状態が続きます。代わりに内容を確認します。実際の検索を実行し、レスポンス本文に想定した単語が含まれるか照合します。Uptime Kuma によるキーワード監視 なら、追加のツールなしでこれを実行できます。バージョンを更新するたびに /stats/errors も監視してください。エンジンの HTML が変更されると、レート制限が関係なくてもパーサーが壊れることがあります。
FAQ
SearXNG をリバースプロキシの背後に置いた後、すべての訪問者に 429 が返るのはなぜですか?
リミッターがプロキシをクライアントとして数えているためです。SearXNG は、接続元アドレスが /etc/searxng/limiter.toml の trusted_proxies に登録されている場合にだけ X-Forwarded-For を読み取ります。登録されていない場合、すべての訪問者が 1 つのカウンターを共有するため、全員が 10 分あたり 150 リクエストの上限を同時に超えます。プロキシの接続元アドレスを追加してください。Docker では通常、ブリッジ範囲の 172.16.0.0/12 です。また、プロキシが X-Real-IP と X-Forwarded-For を送信することを確認してください。管理していない範囲は決して登録しないでください。信頼するネットワークにすると、訪問者がそのヘッダーを任意に設定し、リクエストごとに新しい識別子を選べるためです。
SearXNG のリミッターでは、1 時間あたり何件の API リクエストが許可されますか?
IP アドレスごとに 1 時間あたり 4 件です。HTML 以外の形式を要求するリクエストは、別の 1 時間ウィンドウで数えられます。この制限は limiter.toml ではなく searx/botdetection/ip_limit.py で設定されるため、config から引き上げることはできません。エージェントやスクリプトは、1 つのタスクでこの制限に達します。クライアントのアドレスを limiter.toml の pass_ip に追加するか、リミッターがリクエストを認識しない内部ネットワーク経由でインスタンスにアクセスしてください。
429 エラーがないのに検索結果が空になるのはなぜですか?
拒否しているのはユーザーではなく、サーバーです。自分のインスタンスで /stats/errors を開いてください。失敗した各エンジンとその理由が表示されます。CAPTCHA またはアクセス拒否の項目がある場合、そのエンジンがサーバーの IP アドレスをブロックしています。SearXNG はその後、エンジンを一時停止します。リクエスト過多の応答後は 1 時間、CAPTCHA の後は 1 日停止します。上流でのブロックを解除するローカル設定はありません。そのため、アドレスをブロックするエンジンを削除し、応答するエンジンを残してください。
プライベートなインスタンスでもリミッターを有効にすべきですか?
自分以外からインスタンスにアクセスされない場合は、limiter: false のままにしてください。リミッターを有効にすると Valkey への依存関係が追加され、自分のスクリプトもブロックされます。また、存在しないトラフィックから保護することになります。インスタンスにパブリックアドレスを割り当てたら、public_instance: true と併せて有効にしてください。この組み合わせには意図があります。public_instance: true が設定されていて Valkey が正常に動作しない場合、プロセスは保護されない状態で実行を続けず、ステータス 1 で終了します。