Claude Codeを使っていて、こんなことで困っていませんか。
- 「調べて」と頼んだのに、返ってきたのがどこかで見たようなまとめ記事の受け売りばかりだった
- 検索結果がまるで見当違いで、なぜか関係のない国のスポーツニュースが並んでいた
- 検索そのものが0件で返ってきて、「該当する情報は見つかりませんでした」と丁寧に謝られた
- 存在するはずのものを「見つかりませんでした」と報告されて、後から普通に存在することが判明した
- ライブラリの使い方を調べさせたら、バージョンの古い個人ブログを掴まされた
- そもそも、どの検索エンジンを引いているのか自分でもわかっていない
私も全部やられました。しかも一度や二度ではありません。特に3つめの0件で謝られるやつは精神に来ます。こちらは調べものを進めたくて頼んでいるのに、何もわからないまま時間だけが過ぎていくからです。
結論から言うと、これらの問題の大半は設定で潰せます。正確に言えば、設定と、クエリの書き方と、壊れたときの切り分け手順の3点セットで潰せます。逆に言うと、設定ファイルをいじるだけでは半分しか解決しません。ここを勘違いしたまま設定だけ真似すると、たぶん期待外れに終わります。
この記事では私が実際に自分の環境でやっている「今のClaude Codeの検索設定」を、設定ファイルの中身から運用ルール、失敗の履歴まで含めてまるごと公開します。うまくいった話だけでなく、原因の特定に何日も溶かした話や、対策を打ってから却下された話も書きます。成功例より失敗例の方が役に立つと思っているからです。読み終わるころには、あなたのClaude Codeの検索も今よりだいぶマシになっているはずです。
なおこの記事は2026年8月18日時点の構成をもとに書いています。Claude Code本体もMCPサーバーも更新が速いので、数か月後には細部が変わっている可能性があります。その点だけ先にお断りしておきます。
この記事でわかること
- Claude Codeの検索まわりに何と何があるのか(内蔵のWebSearchとMCP)
- 内蔵のWebSearchが具体的にどう弱いのか(不具合報告の番号つき)
- 内蔵のWebSearchを封じて自前の検索基盤に一本化する具体的なやり方
- 自前SearXNGをMCPでつなぐときの設定と、絶対に踏む罠
- 検索結果が壊滅する原因ナンバーワンと、その二重の対策
- 検索クエリの書き方の、実測に基づいた指針
- 外したクエリを立て直す4つの型
- フックで作法を機械的に強制する方法
- 検索が壊れたときに何をどの順番で見るか
- 導入にかかる費用と手間、そして元が取れるかどうか
この記事の前提となる環境
設定の話は環境に依存します。前提が違うとそのまま真似しても動かないので、先に私の環境を出しておきます。
| 項目 | 内容 |
|---|---|
| クライアント | Windows 11上のClaude Code |
| 検索基盤 | 自分のVPSに立てたSearXNG |
| 接続方法 | MCPサーバー(mcp-searxng 1.15.0) |
| 内蔵WebSearch | 設定で禁止 |
| 併用しているMCP | context7、sequential-thinking など |
| 運用歴 | 2026年7月に構成を刷新、以後継続運用中 |
この記事に出てくる数字や症状は、すべてこの環境で実際に観測したものです。どこかで読んだ話の要約ではありません。エンジンが死んだ日付も、クエリの統計も、実測値をそのまま書いています。
- そもそもClaude Codeの検索まわりはどうなっているのか
- 内蔵のWebSearchはどれだけ弱いのか
- 結論、私は内蔵のWebSearchを切りました
- 自前SearXNGという選択肢
- 導入手順を5ステップで整理する
- languageを書かないと検索結果が壊滅する話
- エンジン構成の話(braveとstartpageが退場するまで)
- 実はクエリの書き方こそが最大の設定です
- 外したクエリを立て直す4つの型
- 返ってきた資料をどう格付けするか
- そもそも検索を使わないという判断
- フックで作法を機械的に強制する
- 検索が壊れたときの切り分け手順
- 検索・ドキュメント・思考の使い分け
- よくある失敗例3選
- 費用と手間の話
- 他の選択肢との比較
- メリットとデメリットを整理する
- こんな人におすすめ、こんな人には不要
- 導入したあとにやっておきたい3つのこと
- よくある質問
- 今後やりたいこと
- まとめ
そもそもClaude Codeの検索まわりはどうなっているのか
設定の話に入る前に前提を整理しておきます。ここを飛ばすと後半の話がぼやけるので、すでに知っている人も軽く目を通しておいてください。逆にここさえ押さえれば、後は応用でしかありません。
Claude Codeが外の情報を取ってくる経路は、ざっくり2つあります。ひとつが内蔵のWebSearchツールで、もうひとつがMCP(Model Context Protocol)経由で外から生やした検索ツールです。前者は最初から入っていて何もしなくても使えます。後者は自分で用意する必要があります。手間だけ見れば前者の圧勝です。それでも私は後者を選びました。理由は追って書きます。
内蔵のWebSearch
内蔵のWebSearchは、Claude Codeが標準で持っている検索ツールです。ユーザーが何も設定しなくても、モデルが必要と判断すれば勝手に呼ばれます。手軽さという一点だけを見れば、これに勝るものはありません。インストール作業もゼロ、設定ファイルをいじる必要もゼロです。
ただし使う側から見ると中身がブラックボックスです。どの検索エンジンを引いているのか、どういう基準で結果を絞っているのか、こちらからは指定できません。検索の中身を自分で決めたい人にとっては、ここが致命的に不自由です。私はまさにその「決めたい人」でした。
手軽さは本物です。ただし中身を見ていくと、不自由なのは「指定できない」ことだけではありませんでした。返ってくるものの形そのものに問題があります。ここは章を分けて具体的に書きます。
そもそもMCPとは何か
MCPという略語が唐突に出てきたので、ここで簡単に説明しておきます。すでに知っている人は読み飛ばしてください。
MCPはModel Context Protocolの略で、AIに外部ツールを生やすための共通規格です。拡張機能の差し込み口だと思ってもらえばだいたい合っています。検索に限らず、ファイル管理でも、カレンダーでも、社内システムでも、MCPサーバーさえ用意すればAIから呼べるようになります。
重要なのは、MCPサーバーが自分の管理下にあるという点です。中で何をしているかはこちらが決められますし、動かなくなったら自分で直せます。この「自分で直せる」という性質が、この記事の後半でずっと効いてきます。
MCPで外部の検索を生やす
検索用のMCPサーバーはいくつも公開されていて、商用の検索APIを叩くものもあれば、自前で立てたメタ検索エンジンを叩くものもあります。私が使っているのは後者、具体的にはSearXNGを叩くmcp-searxngというMCPサーバーです。
MCP経由の何がいいかというと、検索の中身が全部こちらの手の内に入ることです。どのエンジンを使うか、どの言語で引くか、どのカテゴリで束ねるか、全部自分で決められます。逆に何が悪いかというと、全部自分で決めなければならないことです。これはメリットとデメリットが同じものの裏表になっている典型例で、そのぶん最初の設計と、壊れたときの復旧が自分の仕事になります。
2つの経路を表で比べる
| 比較項目 | 内蔵WebSearch | MCP+自前SearXNG |
|---|---|---|
| 導入の手間 | ゼロ | サーバー構築が必要 |
| エンジンの選択 | できない | 自分で決める |
| 言語や地域の指定 | できない | パラメータで明示できる |
| 死んだエンジンの入れ替え | できない | 設定ファイルで差し替え |
| 検索履歴の置き場所 | 外部 | 自分のサーバー |
| 壊れたときの対応 | 待つしかない | 自分で直せる(直さないと直らない) |
| 運用コスト | ゼロ | それなりにかかる |
この表を見ればわかるとおり、両者はトレードオフの関係にあります。手軽さを取るか、制御を取るかという話です。ここで「どちらが正解ですか」と聞かれたら、私の答えは「用途によります」になります。ただし私自身の用途では、後者以外にありえませんでした。
ここまでのまとめ
この章の要点は3つです。まず、Claude Codeの検索経路は内蔵とMCPの2つあること。次に、内蔵は手軽だが中身をいじれないこと。そして、MCP経由なら全部いじれる代わりに全部自分の責任になること。この3点さえ頭に入っていれば、次の章からの話はすんなり入ってきます。
内蔵のWebSearchはどれだけ弱いのか
前の章では「中身をいじれない」とだけ書きました。ここでは実際にどう弱いのかを、報告されている不具合の番号つきで具体的に書きます。ここを飛ばすと、わざわざ自前で立てる理由がぼやけます。
弱点1 渡せるのが検索語だけ
まず、こちらから渡せるものがほとんどありません。言語も、地域も、どのエンジンを引くかも、何件返すかも指定できません。
これは地味な話に聞こえますが、実は致命的です。この記事の後半でさんざん書くことになるlanguageの明示は、そもそも渡す先のパラメータが存在しません。つまり内蔵側では、あの対策を打とうにも打てないのです。
弱点2 返ってくるのが生の検索結果ではない
個人的にいちばん効いたのがこれです。内蔵のWebSearchが返すのは検索結果そのものではなく、あらかじめ要約され整形されたテキストです。
この挙動は不具合として報告されています(issue 2647)。報告の中身は、WebSearchがAIによる要約を混ぜて返すため、どこまでがツールの出力でどこからがモデルの生成なのか区別できないというものです。出典の帰属が曖昧になり、要約の正確さを検証できません。
そしてこの報告は「対応予定なし」として閉じられています。つまり不具合というより、そういう作りだということです。生の結果が欲しければ、内蔵以外の経路を用意するしかありません。
弱点3 検索したのかどうかすら分からない
「Did 0 searches」という表示についての報告が、いくつも上がっています(issue 11369、13546、57566)。
中身は、実際には検索が実行されて結果も返っているのに、表示上は0件と出るというものです。macOSでもLinuxでもWindowsでも報告があります。成功しているのに失敗したように見えるわけです。
この記事の後半で、私は「0件は故障ではない」と何度も書きます。しかしそれを言えるのは、自前側では0件の中身を自分で確かめられるからです。内蔵側にはその手段がありません。
弱点4 キーワードの選び方に手を出せない
ここがこの記事の主題と直結します。先に結論を書くと、この記事で紹介するクエリの作法は、内蔵のWebSearchでは実行できません。
語数を4語に絞るとか、まとめ記事を呼ぶ語を避けるといった作法は、検索語をこちらが握っていて初めて成立します。ところが内蔵側では、モデルが内部で組み立てた語がそのまま飛びます。検査するパラメータもなければ、絞り込むためのパラメータもありません。作法を書いても、それが守られたかどうかを確かめる手段がないのです。
この点についても報告があって、キーワードの選び方が発見性を制限しているという指摘です(issue 20534)。こちらも「対応予定なし」で閉じています。
弱さの本体は「診断できないこと」
4つ並べましたが、根っこは同じです。結果が悪かったときに、なぜ悪かったのかを切り分ける材料が一切ないということに尽きます。
クエリが悪かったのか、上流のエンジンが不調だったのか、そもそもその情報がウェブに存在しないのか。この3つはまったく別の問題で、打つ手も別です。しかし内蔵側では区別がつきません。だから「今日は調子が悪いな」で終わってしまいます。
表にするとこうなります。
| やりたいこと | 内蔵WebSearch | 自前SearXNG |
|---|---|---|
| 言語を固定する | 手段なし | languageで指定 |
| クエリの語数を検査する | 手段なし | フックで検査 |
| 生の検索結果を見る | 要約が混ざる(issue 2647) | そのまま返る |
| 0件の中身を確かめる | 手段なし | APIのJSONを見る |
| どのエンジンが返したか知る | 手段なし | 結果に含まれる |
| 死んだエンジンを外す | 手段なし | 設定ファイルで差し替え |
右の列が全部「手段あり」になっているのが、自前で立てた見返りです。逆に言えば、左の列で困っていない人がわざわざ移る理由はありません。
結論、私は内蔵のWebSearchを切りました
ここからが本題です。私の環境では内蔵のWebSearchを設定で明示的に禁止しています。使わないようにお願いしているのではなく、呼べないようにしてあります。
settings.jsonのdenyに1行
やることは単純で、設定ファイルの権限ブロックに1行足すだけです。ユーザー全体の設定は~/.claude/settings.jsonにあります。私の環境の該当部分を抜き出すと、こうなっています。
{
"permissions": {
"allow": [
"Bash(curl *)",
"Bash(python *)",
"Bash(python3 *)",
"PowerShell(curl *)"
],
"deny": [
"WebSearch"
]
}
}
ポイントはdenyの中の1行です。これだけで内蔵のWebSearchは呼べなくなります。設定としてはこれで終わりで、拍子抜けするほど簡単です。
ついでにallow側でcurlとPythonを通してあるのも地味に効いています。検索がだめでも直接URLを叩く経路が生きているので、詰まったときに手が残ります。検索は通らないがサイトは読める、という状況はけっこう頻繁に起きるので、この2つはセットで考えたほうがいいです。むしろ検索より直読みの方が確実な場面すらあります。販売サイトの在庫や、官公庁の告示や、公式ドキュメントのように「どこを見ればいいか最初からわかっている」情報は、検索を経由するだけ遠回りです。
なぜお願いベースではだめなのか
「使わないでね」と指示ファイルに書いておくだけではだめなのか、と思うかもしれません。私も最初はそれで足りると思っていました。
結論から言うと足りませんでした。忙しいときに限って手近な方に手が伸びるからです。これは人間もAIも変わりません。ルールとして書いてあることと、実際に選ばれる行動は別物です。だから禁止するなら、お願いではなく仕組みで塞ぐ。この考え方はこの記事の後半、フックの章でもう一度出てきます。
切って困らなかったのか
正直に言うと、最初は困りました。自前の検索が不調なときに逃げ道がないからです。しかしこれは想定内の痛みでした。というより、逃げ道を残しておくと自前側の不具合が発見されないまま放置されるので、あえて逃げ道を塞いだというのが正しい説明です。おかげでこの記事の後半に出てくる不具合が全部あぶり出されました。
もし逃げ道を残していたらどうなっていたか。おそらく自前側は不調のまま放置され、いつのまにか内蔵側だけが使われる状態になっていたはずです。そしてVPS代だけ払い続けることになります。これが最悪のパターンです。
今となっては切ったこと自体はまったく後悔していません。ただし切るなら自前側をちゃんと運用する覚悟が要ります。ここは正直に書いておきます。片手間でやると、単に検索できない環境ができあがるだけです。
切る前に確認しておきたいこと
これから同じことをやろうとしている人向けに、順番の話をしておきます。先に自前側を用意してから内蔵を切ってください。逆をやると、検索できない期間が発生します。当たり前のようですが、勢いでやると順番を間違えます。
そして自前側が動いていることを、実際にいくつかクエリを投げて確認してから切ります。「設定を書いたから動いているはず」は禁物です。動いていることの確認と、設定を書いたことは別です。
自前SearXNGという選択肢
ではその「自前側」は何かというと、SearXNGです。ここではSearXNGそのものの説明から、私の実際の構成、MCPの設定ファイルの中身まで順に見ていきます。
SearXNGとは
SearXNGは、複数の検索エンジンの結果をまとめて返してくれるメタ検索エンジンです。自分でインデックスを持っているわけではなく、裏でBingやDuckDuckGoといった既存のエンジンを叩いて、その結果を束ねて返します。オープンソースで、自分のサーバーに立てられます。
ここが重要なところで、SearXNG自体は検索エンジンではなく取りまとめ役です。だから結果の質は上流のエンジン次第になります。上流が調子を崩せば結果も崩れます。この構造を理解しておかないと、後で出てくるトラブルの原因が読み解けません。
なぜ自分で立てるのか
公開インスタンスもたくさんあります。他人が立てたSearXNGを借りて使う形です。無料で使えて、構築の手間もありません。
それでも私が自分で立てたのは、設定ファイルを書き換えられるからです。どのエンジンを有効にするか、死んだエンジンをどれと入れ替えるか、上流に投げるときのパラメータをどうするか。これらは全部サーバー側の設定であって、借り物のインスタンスでは触れません。この記事の後半でやっているエンジンの入れ替えは、公開インスタンスでは手が出せない領域です。
もうひとつ地味な理由があって、検索履歴が自分のサーバーに閉じます。他人のインスタンスを使うということは、自分が何を調べているかを他人のログに残すということです。気にしない人は気にしなくていい話ですが、私は気になりました。
私の構成
私の構成はシンプルです。手持ちのVPSにSearXNGを立て、専用のサブドメインを当てて、Basic認証をかけてあります。認証をかけているのは、他人に踏み台にされて上流のエンジンからBANされるのを防ぐためです。ここは公開インスタンス運用と自分専用運用の分かれ目でもあります。
そしてそのインスタンスをClaude Codeから叩くためにmcp-searxngというMCPサーバーを使っています。npmで配布されているのでnpxから直接動かせます。私の環境で今動いているバージョンは1.15.0でした。npxのキャッシュを実際に見て確認した数字です。バージョンを固定していないので、更新されると自動的に新しい方が使われます。
mcp-searxngの設定
MCPサーバーの設定は~/.claude.jsonのmcpServersに書きます。私の環境の該当部分は、認証情報を伏せるとこうなります。
"searxng": {
"command": "cmd",
"args": ["/c", "npx", "-y", "mcp-searxng"],
"env": {
"SEARXNG_URL": "https://search.example.com",
"AUTH_USERNAME": "(Basic認証のユーザー名)",
"AUTH_PASSWORD": "(Basic認証のパスワード)",
"SEARXNG_DEFAULT_LANGUAGE": "ja"
}
}
Windowsで動かしているのでcommandがcmdになっていますが、macOSやLinuxならここはnpxを直接指定して問題ありません。argsから/cを落として["-y", "mcp-searxng"]にすれば動きます。ここでつまずく人が地味に多いので、書き添えておきます。
そしてこの設定でいちばん重要なのが最後の1行、SEARXNG_DEFAULT_LANGUAGEです。ここが本当に重要なので、章を分けて詳しく書きます。この1行がないと検索結果は本当に壊滅します。大げさに言っているのではありません。あとで実例を出します。
導入手順を5ステップで整理する
ここまでの内容を、実際にやる順番に並べ直しておきます。これから導入する人はこの順番でどうぞ。
ステップ1 VPSにSearXNGを立てる
まずはサーバーです。SearXNGはDockerで立てるのが標準的なやり方で、公式のドキュメントに従えば大きくは詰まりません。必要なスペックは後半の費用の章に実測つきで書きました(結論だけ言うと1GBで足ります)。ここでは詳細な手順は省きますが、この段階でブラウザから検索できることを必ず確認してください。この確認を飛ばすと、後でMCP側の問題なのかサーバー側の問題なのかが切り分けられなくなります。
ステップ2 認証をかける
自分専用にするなら認証は必須です。私はリバースプロキシ側でBasic認証をかけています。踏み台にされると上流のエンジンから見て「大量にリクエストを投げてくる怪しいサーバー」になり、真っ先にBANされます。認証は自分を守るためではなく、上流との関係を守るためだと考えたほうがいいです。
ステップ3 MCPサーバーを登録する
前掲の設定を~/.claude.jsonに書きます。書いたらClaude Codeを再起動して、MCPサーバーが認識されているか確認します。ここで認識されない場合、たいていはJSONの構文エラーかパスの問題です。
ステップ4 languageの既定値を入れる
環境変数SEARXNG_DEFAULT_LANGUAGEを設定します。日本語で使うならjaです。この工程を飛ばすと次の章の地獄に直行しますので、絶対に飛ばさないでください。
ステップ5 指示ファイルにルールを書く
最後に~/.claude/CLAUDE.mdに運用ルールを書きます。「検索時はlanguageを必ず明示する」「クエリは4語まで」といった内容です。設定ファイルで担保できるのは既定値までで、呼び方の作法は指示ファイルの担当になります。
所要時間の目安
すでにVPSを持っていて、Dockerが動く状態なら、ステップ1から5まで半日もあれば終わります。ただしこれは「動く状態になるまで」の時間です。この記事の後半に書いてある罠を全部踏んで、それを直しきるまでの時間は含まれていません。そちらは正直、数週間単位でかかりました。
languageを書かないと検索結果が壊滅する話
この記事でいちばん読んでほしいのがこの章です。というのも、私はこの問題の原因を特定するまでに、かなりの時間を溶かしたからです。同じ穴に落ちる人を減らしたいので、症状から原因、対策まで丁寧に書きます。
症状
症状は主に2つの形で出ます。ひとつは0件。もうひとつがまったく無関係な結果です。厄介なのは後者です。
日本語で普通の調べものをしているだけなのに、返ってくるのがまるで関係のない国のスポーツ記事であったり、動画配信サービスの案内であったり、別の言語のSNSの投稿であったり、車のフォーラムであったりします。何を検索したらそうなるのかと思うでしょうが、本当にそうなります。共通点がまったく見いだせないので、余計に混乱します。
なぜ気づきにくいのか
この症状の何が悪質かというと、まず自分のインスタンスを疑ってしまうところです。認証が切れたのか、サーバーが落ちたのか、エンジンが全滅したのか。私はその全部を疑って全部を調べました。そして全部シロでした。認証も疎通もMCPサーバーもインスタンス本体も、全部正常だったのです。
さらに厄介なことに、ブラウザから同じインスタンスを開いて同じクエリを打つと、ちゃんとした結果が返ってきます。つまり手で確認すると正常に見えるのです。ここで多くの人が「じゃあインスタンスは正常だから、Claude側の問題だろう」と考えて、あさっての方向を調べ始めます。私もそうでした。
原因
原因はlanguageパラメータの欠落でした。2026年7月20日に真因を特定しています。
仕組みはこうです。ブラウザからSearXNGを使うときは、リクエストにAccept-Languageヘッダが自動で乗ります。SearXNGはそれを見て、上流のエンジンに投げるときの言語や地域を決めます。ところがMCP経由で叩くと、このヘッダが乗りません。すると上流のエンジン、特にBingあたりがクエリの解釈に失敗して、無関係な地域向けの検索結果ページを返すか、あるいは空を返します。
つまり壊れていたのはクエリでもインスタンスでもなく、リクエストヘッダの欠落だったわけです。そしてこれは、ブラウザから手で確認しても絶対に再現しません。ブラウザは勝手にヘッダを付けるからです。「手で確認したら正常だった」が、そのまま罠になっていたことになります。
逆に言えば、language: jaを付けるだけで直ります。何の工夫もないプレーンなクエリでも、ブラウザで検索したときと同じような、ちゃんと束ねられた結果が返ってきます。原因がわかってしまえば拍子抜けするほど単純な話なのですが、わかるまでが長かったです。
対策は二重にかける
対策は2つ打ってあります。片方だけだと抜けるからです。
- 環境変数で既定値を入れる。前掲の設定にある
SEARXNG_DEFAULT_LANGUAGE: "ja"がこれです。呼び出し側が何も指定しなかったときの保険になります。 - 指示側のルールに明記する。私は
~/.claude/CLAUDE.mdに「検索時はlanguageパラメータを必ず明示する」と書いています。日本語系のクエリならja、英語圏の調査ならenという具合です。
なぜ二重にするかというと、環境変数の既定値は日本語には効きますが、英語圏を調べたいときには逆に邪魔になるからです。既定値は最後の砦であって、本来は呼ぶたびに明示するのが正しい。だから両方やります。片方だけだと、日本語しか調べられない環境か、あるいは忘れたら壊れる環境のどちらかになります。
この件から得た教訓
教訓は3つあります。
ひとつめは0件を見てもインスタンスの死亡を疑うなということ。0件応答というのは「HTTPは200で返ってきていて、結果の配列が空だった」という意味でしかありません。疎通も認証も正常なのです。ここを混同すると、正常なサーバーを再起動して回るという不毛な時間が発生します。
ふたつめは、手で確認する経路と実際に使う経路が違うなら、手での確認は証拠にならないということ。ブラウザで動いたからといって、MCP経由でも動く保証はどこにもありませんでした。
みっつめは、この二重対策を入れた今となってはlanguage欠落が原因になりえないということです。つまり今後0件を見ても、そこを疑って時間を使うのは無駄になります。潰した原因を疑い続けるのは、地味に時間を食う失敗です。一度潰した原因は候補から外す。これも運用ルールのひとつとして書いてあります。
エンジン構成の話(braveとstartpageが退場するまで)
自前でSearXNGを立てるということは、どのエンジンを使うかを自分で決めるということです。そしてエンジンは普通に死にます。ここは公開インスタンスを使っていると見えない世界なので、詳しく書いておきます。
braveの429
まずbraveが死にました。症状はHTTP 429、つまりレート制限です。しかもこれは私のインスタンス固有の問題ではなく、SearXNGの公開されている不具合報告(issue 6402)にあるとおり、全インスタンス共通の既知の問題でした。自分の設定をいくらいじっても直らない類の故障です。
ここで学んだのは、まず既知の問題を探すという手順の大切さです。自分の設定を疑う前に、同じ症状が他所でも起きていないかを確認する。これをやっていれば、無駄にいじり回す時間が減っていました。
結局これは同じインデックスを引いているresulthunterというエンジンに置き換えて解決しました。ただし日本語のカバレッジはbraveより弱めなので、完全な代替ではありません。それでも動かないものを置いておくよりはるかにマシです。
startpageの敗北
次にstartpageです。こちらはbot判定の壁に阻まれました。データセンター帯のIPアドレスから叩いているのが原因で、こちらの手を尽くしても突破できませんでした。試したのは2つです。
- SSHのSOCKSトンネルを立てて、ブラウザ経由でCAPTCHAを解除してみる
- Cloudflare WARPをサイドカーとして立て、エンジン単位でプロキシを噛ませる
結果はどちらも失敗でした。WARP経由のIPアドレスも同じくbot扱いされます。ここで打つ手なしと結論して無効化しました。公開インスタンスの一部ではstartpageが動いているのですが、動いている側と動かない側の違いは、結局のところ出口IPアドレスの素性だけです。私のVPSの出口IPでは、どうやっても通りません。
この「打つ手なしと実証してから諦める」という工程は、地味ですが重要でした。なんとなく諦めると、後で「あれは試したっけ」と何度も蒸し返すことになるからです。試して駄目だったという記録を残せば、その選択肢は二度と検討しなくて済みます。
ちなみにこのとき出ていたエラーはJSONDecodeErrorでした。一見すると謎のパースエラーですが、これは検索結果ページの抽出に失敗したという意味であり、実質的にはbot判定ページを掴まされた徴候です。パースエラーを見たらパーサーのバグを疑いたくなりますが、この文脈ではbot壁のサインとして読むのが正解でした。
現行のラインナップ
そういった整理を経て、2026年7月20日に構成を刷新しました。今の顔ぶれはこうです。
| エンジン | 状態 | 備考 |
|---|---|---|
| resulthunter | 現役 | braveの後継。主力として働く |
| google cse | 現役 | 刷新時に新規導入 |
| duckduckgo | 現役 | 一度落ちたが復活済み |
| bing | 現役 | languageさえ渡せば安定 |
| wikipedia | 現役 | 事実確認の土台 |
| brave | 退場 | 429の既知問題 |
| startpage | 退場 | 出口IPのbot壁 |
| 素のgoogle | 排除 | 本家でも無効化済み |
| mojeek | 排除 | 刷新時に整理 |
この刷新で何が変わったかというと、「エンジンが全滅している前提」で動かなくてよくなったことです。刷新前は主力が軒並み不調で、検索が失敗するのが常態でした。そういう状態が続くと、失敗を前提とした振る舞いが染みついてしまいます。構成を直したら、その前提も一緒に捨てる必要がありました。
エンジンは常時ちらつく
ここが運用上いちばん大事な感覚なので、強調して書いておきます。エンジンの生死は固定ではなく、常時ちらついています。
2026年8月5日に実際に測ってみました。まったく同じ条件の検索を2分の間隔をあけて2回投げただけで、resulthunterは20件返した後にタイムアウトし、逆にduckduckgoはCAPTCHAで弾かれた後に10件返してきました。つまり2分で立場が入れ替わったわけです。
この事実がわかっていると、運用の手順が変わります。1回のタイムアウトやCAPTCHAを見ただけで「そのエンジンは死んだ」と判定してはいけない。まず同じクエリをもう一度投げる。これが復旧手順の第1歩になります。
エンジンが死んだと判断する基準
ではどうなったら死亡と判断するのか。私が使っている目安はこうです。
- 1回の失敗は無視する。ちらつきの範囲内
- 2回連続で失敗したら、切り分けの手順に入る
- 数日にわたって同じ症状が続き、かつ既知の不具合報告が見つかったら退場を検討する
- 代替エンジンの目処が立ってから外す。空席のまま外さない
最後の項目が大事で、不調なエンジンでも空席よりはマシな場合があります。たまに結果を返すなら、それは戦力です。切るのは代わりが用意できてからにしています。
エンジンを名指しするときの決まりごと
特定のエンジンだけを使いたいときは、クエリの先頭にbangを付けます。主力が落ちているときに、生きているエンジンへ寄せる用途です。ここには踏むと確実に壊れる地雷が2つあります。
- bangは1クエリに1個まで。複数並べると外部bang扱いになって壊れます。
!bing !duckduckgoのような書き方は無効です。 - enginesパラメータは使わない。ツール既定のエンジン選択に任せます。ここを手で指定しはじめると構成の一貫性が崩れます。
絞り込みたいときは、エンジンを名指しするのではなくlanguageやカテゴリで絞る。これが正しい方向です。道具を増やすのではなく、道具の使い分けで絞るという発想になります。
実はクエリの書き方こそが最大の設定です
ここまで設定ファイルの話をしてきましたが、正直に言うといちばん効いたのはクエリの書き方の見直しでした。設定を完璧に整えても、投げるクエリが悪ければ結果は悪くなります。当たり前の話なのですが、当たり前すぎて長いこと放置していました。
語を並べるほど、まとめ記事に当たる
きっかけは「検索のワード7個は並べ過ぎでは」という指摘でした。実際、こんなクエリを何度も投げていました。
国内版 SIMフリー シャッター音 消せる 機種 一覧 無音 撮影 設定
これは典型的に悪いクエリです。しかも本人としては丁寧に条件を並べたつもりでいるので、なおさら悪いです。条件を明確にすればいい結果が返ると信じていました。
なぜそうなるのか
理由ははっきりしています。語を足すほど、そのロングテールに合わせて書かれたSEOまとめ記事が上位に来るからです。
考えてみれば当然で、メーカーの仕様ページや公式ドキュメント、販売サイトの商品ページといった一次資料は、短い正式名称でしか書かれていません。「一覧」も「おすすめ」も「方法」も、一次資料には書かれない語です。ところがまとめ記事は、まさにその長い組み合わせで検索されることを狙って書かれています。長いクエリを投げるということは、まとめ記事を名指しで呼んでいるのと同じことなのです。
つまり「二次三次資料ばかり拾ってくる」という問題を、クエリの側で自分から作り出していたわけです。これに気づいたときは正直へこみました。設定をどれだけ整えても、この癖が残っている限り結果は改善しません。
実測データで裏を取る
感覚論で終わらせたくなかったので、実際のデータで裏を取りました。Google Takeoutから自分の検索履歴を引っ張ってきて、2022年5月から2026年7月までのクエリ60,282件を解析しています。人間である運営者自身が、実際にどう検索しているのかを測ったわけです。結果はこうでした。
| 指標 | 実測値 |
|---|---|
| 1語のクエリ | 43.6% |
| 2語のクエリ | 39.4% |
| 3語のクエリ | 13.3% |
| 4語以上 | 3.4% |
| 文字数の中央値 | 8文字 |
| site:演算子の使用 | 0.16% |
| filetype:やintitle:の使用 | ゼロ件 |
| 連続してクエリを投げ直す間隔の中央値 | 54秒 |
| 結果を1回もクリックしないセッション | 37.3% |
数字から読み取れること
数字を見て自分でも驚きました。1語と2語で全体の83%を占めています。4語以上はたった3.4%しかない。
演算子もほぼ使っていません。site:ですら0.16%で、filetype:やintitle:にいたっては1件もありませんでした。検索が上手な人ほど演算子を駆使するというイメージがありますが、少なくとも実測ではそうなっていません。
そして外したときの行動も特徴的です。演算子で絞るのではなく、54秒でさっさと投げ直しています。1本のクエリを練り上げるより、雑なクエリを何本も投げる方が速いという判断です。この行動は理にかなっていて、検索結果を1分眺めて駄目なら、その方向性自体が間違っている可能性が高い。だったら方向を変えた方が早いわけです。
だから今の運用ルールは「目安4語」にしています。ただしこれは上限であって標準ではありません。標準は1語から2語です。そして「一覧」「おすすめ」「方法」「徹底」「最新」といった、まとめ記事に頻出する語はクエリに入れません。
複数語の固有名詞は区切らない
細かいですが効きます。Nothing PhoneではなくNothingPhone、Xperia 5 VではなくXperia5Vと書きます。
理由は2つあって、ひとつは語数を無駄に食うから。4語の枠のうち3語を製品名で使ってしまっては、肝心の属性語を入れる余裕がなくなります。もうひとつは、区切るとAND条件が緩んで、無関係な記事を拾いやすくなるからです。
ただし型番一点張りも同じくらいまずい
ここは逆方向の失敗なので、あわせて書いておきます。短ければいいというものでもありません。型番だけで検索すると、その文字列を使っているページしか引けません。「国内版SIMフリー」「オープンマーケット版」のような表記ゆれがあると、在庫があっても0件に見えます。
だから型番検索で0件を得たら、そこで「無い」と結論せず、カテゴリページなど別の経路で裏を取る。これをやらないと、存在するものを存在しないと報告する事故が起きます。実際にやらかしました。
要するに語数そのものが問題なのではなく、まとめ記事に最適合する長文クエリと、表記ゆれを取りこぼす一点張りの両方を避けるという話です。両端が危険で、真ん中が安全。そう理解しておくと迷いません。
効果の限界も書いておく
ここは誇張したくないので正直に書きます。クエリを短くしても、話題そのものが商業的なら戻ってくるのはSEO記事のままです。実際に2語の短いクエリで美容系の話題を試したら、ランキング記事と美容外科のコラムがきれいに並びました。
クエリの作法が直すのはクエリの形だけです。返ってきた資料の格付けは別の問題であり、そこは読む側の仕事として残ります。この線引きを曖昧にすると、設定を整えたのに期待外れだったという感想になります。資料の格付けについては、後で章を立てて詳しく書きます。
外したクエリを立て直す4つの型
短いクエリを投げれば、当然ながら一発で当たらないことも増えます。ではどう立て直すのか。ここも実測から型が出ています。
先ほどの6万件のデータで、連続して投げられたクエリのペア42,076件を分類したところ、立て直し方は4つに分かれました。割合つきで並べます。
| 型 | 割合 | 中身 |
|---|---|---|
| 語を足す | 17.1% | 元のクエリに1語だけ追加する |
| 語を差し替える | 17.9% | 版を上げる、場所を振る、対象を固定する |
| 綴りを探る | 8.2% | 表記が曖昧な固有名詞を確定させる |
| 語を削る | 2.5% | 絞りすぎた分を戻す |
型1 語を足す
いちばん素直な立て直しです。ただし足す語は選びます。実測で多かったのはwikiのような資料の種類を指定する語でした。逆に「一覧」「おすすめ」「方法」は、たとえ人間が使っていても私は控えます。足すのは1語までで、2語足したくなったら方向を変えたほうが早いです。
型2 語を差し替える
いちばん割合が高い型です。中身は3つに分かれていて、版を上げる(旧バージョン名から新バージョン名へ)、場所を振る(地名を隣に移す)、そして対象を固定するという動きです。
3つめが特に効きます。地名だけで引くと関係のない話題が混ざるので、駅名まで含めた形に直す。曖昧な語には作品名やゲーム名や地名を足して文脈を固定する。語数を増やすのではなく、1語をより具体的な1語に置き換えるのがコツです。
型3 綴りを探る
これは検索エンジンを表記の正規化器として使う型です。人名や地名や専門用語の綴りが不確かなとき、まず綴りを確定させるためだけの検索を1本投げます。
この工程を飛ばすと何が起きるか。表記ゆれで0件になったのを「存在しない」と誤読します。前の章に出てきた型番一点張りの失敗と、根っこは同じです。曖昧な固有名詞を扱うときは、綴り確定の検索を先に1本入れる。これだけで誤読がかなり減ります。
型4 語を削る
実測ではたった2.5%しかありませんでした。絞りすぎたときに戻す動きなのですが、実際にはあまり使われていません。戻すより打ち直すほうが速いからです。
この数字は運用にも反映させています。クエリが外れたとき、前のクエリを直そうとするより、ゼロから短く打ち直したほうが結果が良いことが多い。前のクエリに引きずられると、外した理由ごと引き継いでしまいます。
何本まで粘るか
これも実測があります。セッションの61%は1本目でクリックに到達し、2本目までで79%に達します。逆に5本目以降まで粘るのは全体の0.7%しかありませんでした。
つまり4本投げて当たらなければ、検索という手段自体が向いていない可能性が高い。そこからは検索を粘るのではなく、直接サイトを読む方向へ切り替えるほうが早いです。
返ってきた資料をどう格付けするか
ここが設定では解決できない領域です。クエリを直しても、返ってきたページの質は自分で見極めるしかありません。
基準は「誰が書く動機を持っているか」
私が使っている判断基準はひとつです。その問いに答える資料を、誰が書く動機を持っているか。これで当たるべき先が決まります。
| 問いの種類 | 当たる先 | 理由 |
|---|---|---|
| 仕様・機能の有無 | メーカー公式 | メーカーしか正確に書けない |
| 在庫・価格・状態 | 販売サイトの実出品 | 相場は実際の出品にしか存在しない |
| 制度・手続き | 官公庁 | 一次情報がそこにしかない |
| 機種を横断する挙動の一覧 | 実地検証系のサイト | 一次資料が構造的に存在しない |
| 相場・おすすめ・ランキング | 使わない | 数値の出所も鮮度も追えない |
一次資料に振り切るのも間違い
ここで一度失敗しています。まとめ記事から相場を拾って叱られた反動で、今度は一次資料だけに振り切ったら、それも違うと言われました。振り子になっていたわけです。
表の4行目がその答えです。複数の機種を横断する挙動の一覧は、そもそも一次資料が存在しません。メーカーは自社の1機種分しか書かないし、他社との違いなど書く動機がないからです。こういう問いでは、実地検証を積んでいる二次資料のほうが正確です。
二次資料を使うときの見分け方も書いておきます。実地検証系かSEOまとめ系かを見分ける。前者は個別に手順とスクリーンショットがあり、検証した環境を明記しています。後者にはそれがありません。
出典の種別は行ごとに書く
これも失敗から学んだことです。比較表を作るとき、節ごとに出典を分けると、その出典を持たない項目が丸ごと落ちます。実際、公式で取れた項目だけを載せて、取れなかった項目を空欄のまま出して叱られました。
正しいやり方は表を埋めたうえで、行ごとに出典の種別を付けることです。「公式で取れなかったから空欄」は使い分けではなく手抜きでした。一次で取れないことと、書けないことは別の話です。
そもそも検索を使わないという判断
検索設定の記事でこう書くのもなんですが、いちばん確実なのは検索を使わないことです。
行き先がわかっているなら直読み
仕様なら公式ページ、在庫なら販売サイト、制度なら官公庁のサイト。行き先が最初からわかっているなら、検索を経由するのは遠回りでしかありません。検索は「どこを見ればいいかわからないとき」の道具です。
だから私の環境ではallowにcurlを入れてあります。前半で「地味に効いている」と書いたのはこの話で、検索を経由しない経路が常に開いている状態を作っておくと、検索の調子に左右されなくなります。
検索が向かない問いの見分け方
目安はこうです。
- 答えが1か所にしか存在しない問い(公式仕様、法令、時刻表)は直読み
- 答えが分散していて、突き合わせが要る問いは検索
- 商業的な話題で、比較や相場が絡む問いは、検索してから販売サイトへ移る
- ライブラリやSDKの使い方は、そもそも検索でも直読みでもなく専用の道具を使う
最後の項目については、後の章で詳しく書きます。
フックで作法を機械的に強制する
さて、ここまでのルールを全部守れれば話は終わりです。しかし守れませんでした。ルールをファイルに書いても破られます。そこでハーネス側で機械的に検知することにしました。Claude Codeのフック機能の出番です。
そもそもフックとは
フックは、特定のタイミングで自分のスクリプトを差し込める仕組みです。ツールが呼ばれる直前、呼ばれた直後、セッションの開始時、応答の終了時など、いくつかのタイミングが用意されています。
重要なのは、これがモデルの外側で動くという点です。指示ファイルに書いたルールは、モデルが読んで従うかどうかという話になります。フックは読まれるかどうかに関係なく実行されます。守られないルールを守らせたいなら、こちらが正解です。
検索ガードのフック
私はツールが呼ばれる直前に割り込んで、クエリを検査するスクリプトを置いています。検査項目は4つです。
- 空白区切りで5語以上になっていないか(上限は4語)
- まとめ記事を呼ぶ語が入っていないか(一覧 / おすすめ / 方法 / 徹底 / 最新 / ランキング / 選び方 / 完全ガイド など)
- languageが未指定、または all になっていないか
- enginesパラメータを使っていないか
引っかかった場合の挙動が肝で、検索そのものは止めません。警告を文脈に差し込み、同時にユーザーにも違反ラベルを見せるだけです。検索は正常に実行されるので、警告と検索結果の両方が同時に手元に来ます。
この「両方が手に入る」という設計が効きます。警告だけ来て結果が来ないと、単に作業が止まるだけです。結果も来るので作業は進み、そのうえで書き方の問題も指摘される。指摘を受けたうえで、その結果が実際に悪かったかどうかを自分の目で確認できます。
拒否から警告へ格下げした理由
実は初版では拒否する作りにしていました。条件に当たったらツール呼び出し自体を却下する設計です。しかしこれは同じ日に却下されました。
理由は単純で、4語というのはもともと目安であって、絶対の禁止ではないからです。目安を遮断に格上げすると、正当なクエリまで通らなくなります。5語必要な場面は普通にありますし、そこで機械的に弾かれると調べものが止まります。
もう少し一般化すると、対策の強度は直したい失敗の種類に合わせて決めるべきだという話です。「ルールが届いていなかった」のと「届いたが無視された」のとでは、必要な対策が違います。前者なら見せ方を工夫すればよく、遮断は要りません。守られなかったから最強の対策を打つというのは短絡でした。ここは設計としての反省点です。
セッション開始時に索引を注入する
もうひとつフックを置いています。セッションの開始時に、記憶ファイルの索引を文脈へ注入するものです。
これを入れた理由が情けないのですが、雑談的な話題だと索引を読み飛ばす事故が起きたからです。「これはプロジェクトに関係ない話題だから」と判断して、過去の知見を参照せずに作業を始めてしまう。話題の軽さで免除される規定などないのに、です。
そして皮肉なことに、雑談的な調べものほどこの記事に書いたクエリの作法を破りやすい。真面目な技術調査では守れるのに、軽い話題だと崩れる。だからこそセッションの開始時点で、強制的に読ませることにしました。
フックを入れて変わったこと
効果はありました。ただし効果の中身は「気づく回数が増えた」であって、「違反しなくなった」ではありません。違反は今でもします。ただ、違反した直後に指摘が入るので、そのターンのうちに投げ直せます。
この差は地味ですが大きいです。後から振り返って反省するのと、その場で直すのとでは、直る速さが違います。
検索が壊れたときの切り分け手順
運用の話です。自前で持つ以上、壊れたときは自分で直すしかありません。手順を決めておかないと、毎回ゼロから悩むことになります。私は次の順番で見ています。
- まず同じクエリをもう一度投げる。前述のとおりエンジンは常時ちらついています。1回のタイムアウトやCAPTCHAは死亡を意味しません。2回連続で不調なら次へ進みます。
- エンジン側かクエリ側かを切り分ける。私は診断用のスラッシュコマンドを用意していて、これ一発で判定できるようにしてあります。ここが分岐点です。
- エンジン側だった場合。主力のエンジンが落ちているなら、少し待つか、bangで生きているエンジンに寄せます。
- クエリ側だった場合。エンジンは健全なのに0件なら、クエリを言い換えます。ここでlanguageを疑ってはいけません。二重に対策済みで構造的に起きえないので、時間の無駄になります。
- 絞り込みは語を足さない。languageやカテゴリで絞るか、いっそ検索をやめて目的のサイトを直接読みます。
この手順で大事なのは2番です。最初に切り分ける。エンジン側とクエリ側では打つ手がまったく違うので、ここを飛ばすと的外れな対処を延々と続けることになります。
生死判定は検索APIのJSONを見る
エンジンの生死を知りたいときは、検索APIをJSON形式で叩いて、その中身を見ます。curlとjqがあれば十分です。
R=$(curl -s -u "$USER:$PASS" \
'https://search.example.com/search?q=test&format=json&language=ja')
# いま落ちているエンジン(理由付き)
echo "$R" | jq '.unresponsive_engines'
# いま結果を返したエンジンと件数
echo "$R" | jq '[.results[]?.engines[]?] | group_by(.) | map({e:.[0], n:length})'
これで「いま落ちているエンジン」と「いま結果を返したエンジン」が両方わかります。前者だけ見ても片手落ちで、後者と突き合わせて初めて全体像が見えます。
ただしunresponsive_enginesはそのクエリ1回分のスナップショットにすぎません。ここに名前が出たからといって、そのエンジンが恒常的に死んでいるとは限らない。何度も書きますが、エンジンはちらつきます。
やってはいけない診断
ここは私が実際に失敗した記録なので、恥を忍んで書きます。同じ失敗をする人が減れば元は取れます。
- 統計ページをjqに食わせる。あれはHTMLであってJSONではありません。パースエラーになります。
- エラーログのページで生死を判定する。これは累積のエラーログであって、現在の生死ではありません。過去に一度例外を吐いた記録が残り続けるだけです。私はこれで主力エンジンを死亡と誤判定し、「いや生きてるやろそいつ」と一蹴されました。実際そのエンジンは20件返していました。
- 退場済みのエンジンの復帰を待つ。braveもstartpageも構成から外れています。待っても復帰しません。
- URL直読みが401を返したのを見て、インスタンスが落ちたと判断する。これはBasic認証のヘッダを送れていないだけです。検索そのものは通ります。
共通しているのは観測しているものと結論がずれているという点です。累積ログを現在の状態として読み、認証エラーを疎通エラーとして読む。診断の道具を間違えると、正常なものを壊れていると判定して、あさっての方向の対策を打つことになります。
とりわけ厄介なのが2番目です。累積のエラーログは、見た目には立派な証拠に見えます。エンジン名が並び、例外の種類が並び、いかにも「この子が犯人です」という顔をしている。しかし時間軸が入っていないので、現在の話なのか半年前の話なのかがわかりません。
切り分けを1コマンドにまとめる
ここまでの診断は、毎回手で打つと面倒です。だから私はスラッシュコマンドとして登録してあります。1コマンドで「いま落ちているエンジン」と「いま返しているエンジン」が出るようにしてあるので、迷ったらまずそれを打ちます。
面倒な手順は必ず飛ばされます。飛ばされたくないなら、面倒でなくする。これは検索に限らず、運用設計の一般則だと思っています。
検索・ドキュメント・思考の使い分け
もうひとつ大事な話があります。私の環境では検索用のMCPだけでなく、用途の違うMCPを併用しています。というのも、全部を検索で解こうとするのが間違いだったからです。
| 調べたいこと | 使うもの |
|---|---|
| ライブラリやSDKのAPI仕様、設定方法、バージョン差分 | context7(検索ではなく公式ドキュメント) |
| 一般知識、言語仕様、基礎概念、時事、相場 | SearXNG(検索) |
| 設計上の選択肢比較、判断基準が3つ以上ある問題 | sequential-thinking(段階的推論) |
なぜ分けると効くのか
ここを分けた効果は、思っていたより大きかったです。ライブラリの使い方を検索エンジンで調べると、バージョンの古い個人ブログを掴まされる確率が跳ね上がります。
理由は検索エンジンの性質にあります。検索エンジンは「よく読まれているページ」を上位に出しますが、ライブラリのドキュメントにおいてよく読まれていることと最新であることは無関係です。むしろ古いバージョンの記事ほど長く読まれ、被リンクを集め、上位に居座ります。だから検索で引くと古い情報に当たりやすい。構造的にそうなっています。
公式ドキュメントを直接引ける道具があるなら、そちらが常に優先です。検索は「どこを見ればいいかわからないとき」の道具であって、行き先がわかっているなら使う必要がありません。
呼ぶかどうかで迷わないようにする
この振り分けは、指示ファイルに「該当したら確認を求めず即座に呼べ」と書いてあります。いちいち「使いますか」と聞かれると、それだけで判断のコストが乗るからです。
判断の基準は単純で、空振りのコストより、必要なときに呼ばないコストの方が高い。だから迷ったら呼ぶ側に倒します。呼びすぎて損することは、実際にはほとんどありませんでした。
よくある失敗例3選
ここまでの内容から、特に踏みやすい失敗を3つ抜き出しておきます。この3つを避けるだけでも、だいぶ時間が節約できるはずです。
失敗1 0件を見てインスタンスの故障を疑う
いちばんよくやります。0件は「結果が空だった」という意味でしかなく、サーバーの故障ではありません。それなのに、なぜかまずサーバーを疑います。再起動しても0件は0件のままです。
失敗2 長いクエリで絞り込もうとする
結果が悪いとき、条件を足せば改善すると考えてしまいます。しかし実際には、足した条件のぶんだけまとめ記事に近づきます。絞るなら語ではなくパラメータで絞る。これを覚えておくだけで結果が変わります。
失敗3 累積のエラーログを現在の状態として読む
これも実際にやりました。過去のエラー記録を見て「このエンジンは死んでいる」と判定し、実際にはそのエンジンが主力として20件返していたという間抜けな話です。時間軸のないデータで現在を語らない。教訓としてはこれに尽きます。
費用と手間の話
現実的な話もしておきます。この構成にはコストがかかります。
かかるお金
VPS代がかかります。ただし私の場合、SearXNGのためだけにVPSを借りたわけではありません。もともと他のサービスを動かしているサーバーに相乗りさせているので、追加費用としては実質ゼロです。
逆に言えば、これのためだけにVPSを借りるなら費用対効果はかなり微妙になります。すでにサーバーを持っている人向けの構成だと考えたほうがいいでしょう。それでも借りるという人のために、実測をもとにした必要スペックを次に書いておきます。
それでも借りるなら、実際どれだけ要るのか
先に立場をはっきりさせておきます。私はこの記事のためにサーバーを借り直してはいません。SearXNGは前から動いている既存のVPSに相乗りさせたままで、乗り換えの予定もありません。ここから書くのは「これから借りる人がどのくらいのプランを選べばいいのか」の話であって、私の乗り換え体験談ではありません。
なお、この節にはアフィリエイトリンクを含みます。リンクを踏まれると私に報酬が入る立場で書いているということです。だからこそ数字は実測を出します。
実際に測ってみた
うちのSearXNGは20日連続で動いています。その状態でメモリ使用量を測るとこうでした。
$ docker stats --no-stream
NAME MEM USAGE / LIMIT MEM %
searxng-core 122.3MiB / 3.818GiB 3.13%
searxng-valkey 924KiB / 3.818GiB 0.02%
合計で123MBです。個人で使うぶんには、SearXNGはこの程度しか食いません。前のほうで書いたとおりSearXNGは自分でインデックスを持たず、上流に投げて返ってきたものを束ねるだけなので、当然といえば当然の数字です。
だから安いプランで足ります
この数字をもとにプランを選ぶとこうなります。ConoHa VPSの料金を例に並べます(まとめトク、2026年8月時点。公式の料金ページで確認した実額です)。
| メモリ | 月額(初回) | 更新時 | SearXNG用途での評価 |
|---|---|---|---|
| 512MB | 460円 | 460円 | 動くが余裕がない。SSDも30GBしかない |
| 1GB | 763円 | 763円 | これで足ります |
| 2GB | 793円 | 1,259円 | 他のものと同居させるなら |
| 4GB | 1,380円 | 2,189円 | 要りません |
見てほしいのは更新時の列です。2GBは初回793円ですが、更新時は1,259円に上がります。一方で1GBは763円のまま変わりません。つまり長く使うなら、1GBと2GBの差は月30円ではなく月500円ほどです。初回料金だけ並べた比較を見ていると、ここを取り違えます。
実測が123MBなのですから、SearXNGを単体で動かすなら1GBで十分です。4GBは要りません。私が4GBを使っているのは、同じサーバーで他のものをいくつも動かしているからであって、SearXNGのためではありません。
他社でも事情は変わりません。さくらのVPSでも、XServer VPSでも、この用途なら最小構成で足ります。どこを選ぶかより、大きいプランを掴まされないことのほうが効きます。
プランより先に確認しておくこと
- 出口IPの素性は選べません。前の章でstartpageが通らなかったのは、データセンター帯のIPだからでした。これはどのVPSを選んでも同じ壁に当たる可能性があります。ここだけは金では解決しません
- 他のものと同居させるなら1GBでは足りません。SearXNGが123MBでも、同居する相手が食います
- 512MBを選ぶならスワップの設定が要ります。SSDも30GBに落ちるので、ログを貯める用途には向きません
- 時間課金があるなら、まず数日試してから長期契約に切り替えるのが安全です。この記事の構成が自分に合うかどうかは、実際に動かさないと分かりません。ConoHa VPSは1.3円/時間からの時間課金があるので、数日動かして駄目なら捨てられます
かかる時間
構築そのものは半日です。問題はその後で、この記事に書いた罠を踏み、原因を特定し、対策を打つまでに数週間かかりました。時間の大半は構築ではなく運用に消えます。
ただしこの記事を読んだ人は、そのぶんを短縮できるはずです。少なくともlanguageの罠とエンジンのちらつきについては、もう書いてあります。
元は取れるのか
私の結論は「取れた」です。ただし取れた理由は速度でも安さでもなく、失敗の原因が特定できるようになったことにあります。
ブラックボックスのままだと、結果が悪いときに「今日は調子が悪いな」で終わってしまいます。中が見えると、エンジンが落ちているのか、クエリが悪いのか、そもそも検索に向かない問いなのかが分かれます。原因が分かれば次から避けられる。この積み重ねが効きました。
逆に言えば、原因の特定に興味がない人にとっては元が取りにくいと思います。ここは正直に書いておきます。
他の選択肢との比較
公平を期すために、他の選択肢も並べておきます。自前SearXNGだけが正解ではありません。
| 選択肢 | 費用 | 制御 | 向いている人 |
|---|---|---|---|
| 内蔵のWebSearchのまま | なし | なし | 特に不満がない人 |
| 公開SearXNGインスタンス | なし | クエリ側だけ | 手軽に試したい人 |
| 商用の検索API | 従量課金 | 中程度 | 安定性を金で買いたい人 |
| 自前SearXNG | 月763円〜 | 全部 | 原因まで自分で追いたい人 |
個人的な感想を言えば、公開インスタンスから始めるのが無難です。それで不満が出たら自前に移ればいい。最初から自前で立てると、何が不満だったのかを忘れたまま構築だけして満足しがちです。
メリットとデメリットを整理する
メリット
- エンジン構成を自分で決められる。死んだエンジンを外し、代替を入れられる
- 言語を明示できるので、結果が地域や言語で崩れなくなる
- 検索履歴が自分のサーバーに閉じる
- 壊れたときに原因を特定できる。ブラックボックスではないので調べようがある
- 不具合の原因が自分の理解として残る。次に似た症状が出たとき速い
- クエリの作法をフックで検査できる。呼び出しの中身が見えているから可能になる
デメリット
- サーバーの構築と維持が必要。VPS代もかかる
- 上流のエンジンが死ぬ。そして自分で気づいて自分で直すしかない
- この記事で書いたような罠を、ひととおり自分で踏むことになる
- 設定を整えても、商業的な話題では結局SEO記事が返ってくる
- 調子が悪いときに逃げ道がない(内蔵を切っている場合)
こんな人におすすめ、こんな人には不要
おすすめできる人
- すでにVPSを持っていて、サービスを1つ増やすことに抵抗がない人
- 検索結果の質に対して具体的な不満があり、それを言語化できている人
- 結果が悪かったときに、原因まで追いたい人
- 壊れたときに自分で切り分けるのが苦にならない人
やめておいたほうがいい人
- サーバー運用そのものが目的化しそうな人。維持コストが地味に効いてきます
- 「検索の質が上がる魔法」を期待している人。上がるのは制御の幅であって、質は使い方次第です
- 内蔵のWebSearchで特に困っていない人。困っていないなら変える理由がありません
導入したあとにやっておきたい3つのこと
構築が終わってからが本番です。私が後から追加してよかったものを3つ挙げます。
- 診断用のコマンドを1つ作る。面倒な手順は飛ばされるので、1コマンドにまとめます。
- クエリの作法を指示ファイルに書く。設定では担保できない部分です。
- 破った記録を残す。どういう場面で作法が崩れるかがわかると、対策の打ちどころが見えます。
3つめが意外と効きました。失敗の傾向がわかると、対策の強度を適切に選べます。届いていなかったのか、届いたが無視したのか。この区別を記録しておくと、無駄に強い対策を打たずに済みます。
よくある質問
Q. 内蔵のWebSearchを切らないとだめですか
だめではありません。併用もできます。ただし併用すると、自前側が不調なときに黙って内蔵側へ逃げるので、自前側の不具合に気づくのが遅れます。私はあえて逃げ道を塞ぐ側を選びました。切るかどうかは、自前側をどれくらい本気で運用するかで決めてください。
Q. 公開のSearXNGインスタンスではだめですか
用途によります。普通に検索したいだけなら公開インスタンスで足ります。ただしエンジンの取捨選択は設定ファイルを書き換えないとできないので、死んだエンジンを自分で入れ替えたいなら自前で立てるしかありません。
Q. languageを付け忘れるとどうなりますか
0件になるか、まったく無関係な地域の検索結果が返ってきます。私の環境では環境変数で既定値を入れてあるので今は起きませんが、これがない状態で日本語のクエリを投げると、本当に見当違いのものが返ってきます。最初に対策すべきはここです。
Q. ブラウザでは正常なのにClaude経由だと変です
それがまさにlanguageの症状です。ブラウザはAccept-Languageヘッダを自動で付けますが、MCP経由では付きません。ブラウザでの確認は証拠になりません。同じ条件で比べたければ、APIを直接叩いて確認してください。
Q. bangを複数並べてはいけないのはなぜですか
複数並べると外部bangとして解釈されて壊れるからです。1クエリに1個までと覚えてください。複数のエンジンを同時に使いたいなら、bangではなくカテゴリで指定します。
Q. 検索が0件でした。インスタンスが落ちていますか
落ちていません。0件応答は「HTTPは200で返っていて、結果の配列が空だった」という意味です。疎通も認証も正常です。まず同じクエリをもう一度投げてください。それでもだめなら、エンジン側かクエリ側かを切り分けます。
Q. エンジンが落ちたらすぐ外すべきですか
すぐには外しません。1回の失敗はちらつきの範囲です。数日続き、かつ既知の不具合が見つかってから検討します。そして代わりを用意してから外すのが鉄則です。空席にすると結果の総量が減ります。
Q. クエリは何語までにすべきですか
目安は4語です。ただしこれは上限であって、標準は1語から2語です。実測では自分の検索の83%が1語か2語でした。短くして外したら、絞るのではなく投げ直すのが正解です。
Q. 外したとき、前のクエリを直すべきですか
直すより打ち直すほうが速いです。実測でも語を削って戻す動きは2.5%しかありませんでした。前のクエリに引きずられると、外した理由ごと引き継ぎます。ゼロから短く打ち直してください。
Q. 何本投げても当たりません
4本が目安です。実測では2本目までで79%が着地し、5本目以降まで粘るのは0.7%でした。それ以上粘るくらいなら、検索という手段自体を疑って直読みに切り替えるほうが早いです。
Q. フックで検索を止める設定にすべきですか
おすすめしません。私は一度そうして、その日のうちに戻しました。目安を遮断に格上げすると、正当なクエリまで通らなくなります。警告にとどめるのが実用的です。
Q. 結局いちばん効いた設定は何ですか
2つあります。ひとつはlanguageの明示。これは壊滅を防ぐ設定です。もうひとつはクエリを短くしたこと。こちらは質を上げる設定です。ファイルをいじる設定と、書き方を変える設定の両方が要るというのが、この記事の結論のひとつです。
今後やりたいこと
現状で満足しているわけではありません。残っている課題を3つ書いておきます。
- エンジンの生死を定期的に記録して、ちらつきの傾向を数字で持ちたい。今は必要になったときに測っているだけです
- 資料の格付けを自動化したい。クエリの形は直せても、返ってきた資料の質は今のところ手作業で判断しています
- クエリの立て直しの型を、フック側からも支援したい。今は警告を出すだけで、次の一手までは提案していません
とはいえ、足りないものを数えるより、動いているものを壊さない方が優先だとも思っています。ここまでの構成は何度も壊して直してきたので、しばらくは安定運用に徹するつもりです。
まとめ
長くなったので、要点をもう一度まとめておきます。
- 内蔵のWebSearchは要約を混ぜて返す。生の検索結果は返ってこない(issue 2647、対応予定なしで閉止)
- 「Did 0 searches」の報告が複数あり、成功と失敗が表示から区別できない
- クエリの形を決めるパラメータが無いので、この記事の作法は内蔵のままでは実行できない
- 内蔵のWebSearchは
~/.claude/settings.jsonのdenyに1行足せば封じられる - 代わりに自前のSearXNGをmcp-searxng経由でつないでいる(手元のバージョンは1.15.0)
- MCP経由だとAccept-Languageが乗らないので、
languageの明示は必須。環境変数と指示ファイルの二重で担保する - ブラウザで確認しても再現しない。手での確認は証拠にならない
- エンジンは死ぬ。braveは429で、startpageは出口IPのbot壁で退場した
- エンジンの生死はちらつく。2分で20件とタイムアウトが入れ替わった実測がある
- bangは1クエリ1個、enginesパラメータは使わない
- クエリは目安4語、標準は1語から2語。実測では自分の検索の83%が1語か2語だった
- 「一覧」「おすすめ」「方法」といった語はまとめ記事を呼ぶので入れない
- ただし型番一点張りも危険。表記ゆれで在庫があっても0件に見える
- 外したら直すより打ち直す。語を削って戻す動きは実測で2.5%しかない
- 4本投げて当たらなければ、検索ではなく直読みへ切り替える
- 資料の格付けは「誰が書く動機を持っているか」で決める
- 作法はフックで機械的に検知する。ただし遮断ではなく警告にとどめる
- 生死判定は検索APIのJSONを見る。統計ページはHTMLだしエラーログは累積値なので使えない
- ライブラリの仕様はcontext7、思考はsequential-thinkingへ振り分ける
- SearXNGの実測メモリは123MB。借りるなら1GBで足りるし、4GBは要らない
ここまで書いてきて改めて思うのは、設定ファイルの行数より運用ルールの方がずっと長いということです。denyに1行足すのは10秒で終わります。しかしその後ろに、languageの罠があり、エンジンの生死があり、クエリの作法があり、資料の格付けがあり、切り分けの手順があります。この記事の分量のほとんどは後者に使われました。
いかがでしたでしょうか。検索設定というと設定ファイルの話だと思われがちですが、実際に効いたのは書き方と手順のほうでした。ただし正直に書いておくと、ここに書いた対策は内蔵のWebSearchのままでは1つも効きません。言語を固定するにも、クエリの形を検査するにも、渡す先のパラメータが要るからです。まずは公開インスタンスでも構わないので、検索の中身が見える経路を1本作るところから始めてみてください。そこまで来れば、あとはこの記事の手順をなぞるだけです。
合わなければ時間課金で数日試して捨てられます。
