SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-09-05

SearXNGをセルフホストする方法と設定

Docker ComposeでSearXNGをVPSに構築します。settings.yml、limiter、nginxのTLS設定、スクリプトから呼べるJSON検索APIまで、プライベート運用の要点を解説します。

構築するもの

SearXNG をセルフホストすると、自分のサーバーで動作するプライベート検索エンジンを利用できます。SearXNG はメタ検索エンジンです。入力した検索語を Google、Bing、DuckDuckGo、Wikipedia などの検索エンジンに送信し、返された結果を1つの結果ページにまとめます。検索語を保持するマシンは自分のものだけなので、プロファイルは作成されず、追跡 Cookie も設定されません。単に Searx と呼ばれていた古いガイドを見つけた場合、それはこのプロジェクトのフォーク元です。2023年以降コミットがないため、どちらを使うか決める前に 両方の状態を確認してください

構成は小規模です。コンテナ2つ、設定ファイル1つ、リバースプロキシ1つで構成します。小規模な VPS でも問題なく運用できます。ただし、すべてのセルフホストサービスが同じとは限りません。PhotoPrism と Immich を比較したフォトライブラリでは、必要な RAM の下限を Web アプリではなくインデクサーが決めます。実際に決めるべきなのは、インスタンスをプライベートにするか、パブリックにするかです。プライベートとは、自分と自分のスクリプトだけがアクセスできる状態です。パブリックとは、インターネット上の誰でも検索できる状態です。この選択によってセキュリティ設定が変わるため、コマンドを入力する前に決めてください。デフォルトではプライベートにします。

もう1つの導入理由があります。SearXNG のインスタンスは JSON を扱うため、作成したスクリプトや AI エージェントから、自分で管理する検索 API を利用できます。キーは不要で、検索ごとの課金もなく、クォータに関する通知メールも届きません。

Docker Compose で SearXNG をインストールする

このプロジェクトは、コンテナイメージと Compose ファイルを公開しています。Docker Engine と Compose plugin がすでにインストールされている、新規の Ubuntu 24.04 サーバーに両方を取得します。Docker を初めて使う場合は、まず VPS での Docker Compose の基本 を参照してから戻ってきてください。

sudo install -d -o "$USER" -g "$USER" -m 750 /opt/searxng
cd /opt/searxng
mkdir -p core-config
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 .env

Compose ファイルでは、2 つのサービスを定義しています。core は SearXNG 本体で、valkey はレート制限と短時間だけ保持する状態情報に使うインメモリデータストアです。./core-config/ をコンテナ内の /etc/searxng/ にマウントするため、設定する内容はすべてホスト上のその 1 つのディレクトリに保存されます。

次に .env を編集します。配布されているサンプルでは、すべての行がコメントアウトされています。そのため、コンテナはすべてのアドレスでポート 8080 を待ち受けます。次の 3 つの設定をコメント解除して値を指定します。

SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080

重要なのは SEARXNG_HOST=127.0.0.1 です。公開ポートを [::]:8080:8080 ではなく 127.0.0.1:8080:8080 にするため、コンテナは loopback アドレスでのみ応答し、インターネットから直接到達できなくなります。これを省略すると、コンテナの起動直後から外部公開されます。公開した Docker ポートは、ファイアウォールのルールより前段に挿入されるためです。この落とし穴については、Docker の公開ポートは ufw を迂回する で詳しく説明しています。

SEARXNG_VERSION=latest は、学習中であれば問題ありません。ただし、重要なサーバーではタグを固定してください。2026 年 7 月時点で、リリースタグは日付ベースで、2026.3.25-541c6c3cb のような形式です。タグを固定すると、レジストリの変更に応じて自動的に更新されず、自分で決めたタイミングでアップグレードできます。サーバー上で長期間稼働させる他のソフトウェアでも、同じ運用が有効です。そのため、自己ホスト型 RustDesk リレー でもイメージタグを固定しています。リモートアクセスサービスが無人でアップグレードされると、最も都合の悪いタイミングで変更に気付くことになるためです。

settings.yml: 重要な設定項目

初回起動前に core-config/settings.yml を作成します。use_default_settings: true により、SearXNG は同梱のデフォルト設定を読み込んだ後、ファイルに記述したキーだけを適用します。そのため、設定ファイルを短く保てます。また、新しいオプションが追加されるアップグレードにも対応しやすくなります。

まず Secret を生成します。この値をそのままファイルに記述するためです。

openssl rand -hex 32
use_default_settings: true

general:
  instance_name: "search.example.com"

server:
  base_url: "https://search.example.com/"
  secret_key: "paste-the-openssl-output-here"
  limiter: false
  public_instance: false
  image_proxy: true

valkey:
  url: valkey://valkey:6379/0

search:
  safe_search: 0
  autocomplete: "duckduckgo"
  formats:
    - html
    - json

secret_key はセッションとトークンのデータに署名します。同梱のデフォルト値は文字列 ultrasecretkey です。このままにすると、そのデフォルト値を知っている人がトークンを偽造できます。1 度だけ置き換え、その後は変更しないでください。後から変更すると、保存済みの設定がすべて失われます。

base_url には、末尾のスラッシュを含む公開 HTTPS アドレスを指定します。SearXNG はこの値を、生成するリンクに書き込みます。localhost のままにすると、リモートブラウザーで「次のページ」リンクが閲覧者自身のマシンを指し、失敗します。

formats は、Web エンドポイントが生成する出力形式を決定します。json はデフォルトの一覧に含まれないため、追加しないと JSON リクエストが 403 を返します。image_proxy: true を有効にすると、検索結果のサムネイルがサーバー経由になります。そのため、画像をホストしているサイトから訪問者のアドレスを見られなくなります。

valkey.url ではホスト名 valkey を使用します。Compose ファイルでのサービス名がこの名前であり、Compose は両方のコンテナを同じネットワークに配置して、サービス名を名前解決できるようにするためです。localhost を指定すると、リミッターが失敗します。core コンテナ内では、localhost がそのコンテナ自身を指すためです。

Secret は平文ファイルに保存されるため、ファイル自体ではなく、その親ディレクトリを保護します。chmod 750 /opt/searxng により、ホスト上の他のユーザーを締め出せます。core-config/settings.yml を mode 600 に変更して、権限をさらに厳しくしないでください。コンテナは専用の非特権ユーザーで実行されるため、そのユーザーがファイルを読めないと SearXNG は起動できません。

スタックを起動して確認します。

cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/

docker compose ps には、両方のコンテナが running 状態で表示されるはずです。curlHTTP/1.1 200 OK を返すはずです。何も返さない場合は docker compose logs core を確認します。settings.yml の YAML に誤りがあると、該当行を示す解析エラーがそこに表示されます。

nginx の背後に配置して TLS を有効にする

コンテナは loopback でのみ待ち受けるため、nginx によって外部から到達可能になります。nginx は transport layer security (TLS) も追加します。/etc/nginx/sites-available/searxng を記述します。

server {
    listen 80;
    server_name search.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/searxng /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d search.example.com

nginx -t は reload 前に syntax is oktest is successful を出力します。Certbot は同じファイルを書き換え、証明書を使って 443 番ポートで待ち受ける設定にし、80 番ポートからのリダイレクトも追加します。証明書の認証局は HTTP 経由でファイルを取得して所有権を確認するため、search.example.com の DNS レコードは事前にこのサーバーを指している必要があります。更新手順を含む詳しい手順は、Ubuntu 24.04 向けの Certbot と nginx のガイドにあります。

2 つの転送ヘッダーは単なる装飾ではありません。X-Forwarded-ForX-Real-IP がないと、SearXNG に到達するすべてのリクエストにプロキシのアドレスが設定されます。そのため、レートリミッターはすべてのトラフィックを 1 つのクライアントによるものと認識し、訪問者を区別できません。

スクリプトとエージェントが JSON 検索 API を必要とする理由

jsonformats で使用すると、ページを表示するエンドポイントが構造化データも返します。

curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
  | jq -r '.results[0:5][] | .url'

urltitlecontent と、それを提供したエンジンを各エントリに含む results 配列を持つオブジェクトが返されます。さらに answersinfoboxessuggestions も含まれます。これだけで、要約処理、リンクチェッカー、調査ループに結果を渡せます。これらの結果を言語モデルに渡す作業は、見た目以上に大きな一歩です。検索結果は信頼できないテキストであり、独自の指示を含む可能性があるためです。その仕組みについては、SearXNG インスタンスを AI エージェントに指定するで詳しく説明しています。

これはエージェント型の処理全般に関係します。言語モデルには学習データのカットオフがあるため、現在の情報に関する質問へ回答するにはライブ検索が必要です。一方、商用検索 API はクエリごとに課金され、レート制限も厳しく設定されています。ローカルインスタンスなら、すでに料金を支払っているサーバー上で 1 つのコンテナを動かすだけで済み、クエリが外部に出ることもありません。モデルにツールを組み込む場合も同じ考え方が当てはまります。検索ツールは、VPS 上で MCP サーバーを実行する際に最初に追加されることが多いツールです。

API を使用する際のルールは 2 つあります。まず、インスタンスを非公開にしてください。API 側を loopback アドレスまたはプライベートネットワークに bind し、自分のホストだけがアクセスできるようにします。次に、控えめな頻度でクエリを送信してください。SearXNG はリクエストを実際の検索エンジンへ転送するため、スクリプトが 1 秒あたり 100 件のクエリを実行すると、Google にサーバーをブロックさせることになります。

公開インスタンスでのリミッターと設定の変更

リミッターは SearXNG のボット対策機能です。リクエストヘッダー、アドレス、リクエストレートを監視し、自動化されたものに見えるネットワークトラフィックを破棄します。この状態を保持するために Valkey が必要であり、そのため Compose ファイルに含まれています。

プライベートインスタンスでは limiter: false を維持します。独自のスクリプトは定義上、自動化されたトラフィックです。そのため、リミッターは、このインスタンスを用意した目的である JSON 呼び出しをまさにブロックします。アクセス制御はリバースプロキシに任せます。nginx の location に設定する allowdeny の組み合わせ、HTTP Basic 認証、または他のサーバーからの接続だけを許可するファイアウォールを使用します。ネットワーク間を移動するラップトップからプライベートインスタンスに接続する必要がある場合は、前段に v3 onion アドレスを配置する方法もあります。tor はインターネットに新たな公開対象を追加せず、同じ loopback ポートに接続できるためです。

他のユーザー向けにインスタンスを公開する場合は、両方のスイッチを有効にします。

server:
  limiter: true
  public_instance: true

より細かい制御は core-config/limiter.toml で行います。この設定はコンテナが /etc/searxng/limiter.toml から読み込みます。変更するキーだけを記述します。プロキシの背後で運用する場合は、プロキシを宣言する必要があります。宣言しないと、リミッターは nginx のアドレスを不正なクライアントのアドレスとして扱います。

[botdetection]
trusted_proxies = [
  '127.0.0.0/8',
  '::1',
]

[botdetection.ip_limit]
link_token = true

link_token = true を有効にすると、SearXNG は実際のブラウザーセッションだけが取得するトークンを発行します。これにより、単純なスクレーパーの大半を停止できます。公開インスタンスには数日以内にスクレーパーが集まると考えてください。エンジンエラーも発生します。転送するトラフィックが増えるほど、上流の検索エンジンがサーバーのアドレスに CAPTCHA を返し始めるまでの時間が短くなるためです。公開 SearXNG インスタンスの運用は継続的な作業です。プライベートインスタンスではその必要がありません。そのため、2026 年にセルフホストする価値があるものの短いリストの多くに含まれています。これらのリストにある項目がすべてインフラストラクチャとは限りません。Jellyfin のライブラリを歩いて回れる 90 年代のレンタル店として再構築する場合も、同じ nginx ブロックの背後に同じ 1 つのコンテナを配置し、ワークフローではなく夜の時間に向ける構成です。

検索結果が何も返らない場合

インスタンス上で /stats を開きます。ここには各エンジンのエラー率と応答時間が一覧表示されます。検索結果が少ないと感じたときは、最初に確認する場所です。

「Access denied」または「CAPTCHA」エラーが表示されるエンジンは、サーバーのアドレスをブロックしています。これはデータセンターのアドレス範囲でよく起きます。検索エンジンが、それらをスクレイパーのものと判断するためです。SearXNG は失敗したエンジンを再試行せず、一定期間停止します。そのため、ブロックされたエンジンが検索結果からひそかに除外されます。settings.yml で無効にするか、そのエンジンが使えなくなることを受け入れてください。ただし、対処方法はこの2つだけではありません。再起動後も有効な修正で解決できる CAPTCHA ブロックもあります。残りのエンジンは引き続き応答します。429 は判断が難しいケースです。自分のレートリミッターが返している場合と、上流のエンジンがサーバーからのアクセスを拒否している場合があるためです。設定を変更する前に、ログの行を確認すれば、どちらなのか分かります

すべてのエンジンが同時に失敗する場合、コンテナで外向きの名前解決が機能していないか、インターネットへの経路がありません。コンテナの内部からテストします。

docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo ok

このチェックが失敗し始めても、サーバー上のどの機能も通知しません。そのため、cron から実行し、結果が少なくなったことに気付くまで待たずに、自分の ntfy server からスマートフォンへアラートを送信します。

FAQ

SearXNG によって検索は匿名になりますか?

検索先のエンジンから見た場合、リクエストを送信しているのはブラウザーではなくサーバーなので、利用者が誰かは隠されます。ただし、検索内容がサーバーから隠されるわけではなく、サーバー自体が検索先のエンジンから隠されるわけでもありません。単一ユーザー用のインスタンスでは、そのアドレスからのすべての通信が自分のものになるため、アドレス自体が識別子になります。ブラウザーとインスタンス間の通信は TLS 証明書によって保護されます。ISP、パブリックインスタンスの運用者、検索先のエンジンに対してどこまで隠されるかは、SearXNG が実際に隠す情報で説明しています。

JSON リクエストが 403 Forbidden を返すのはなぜですか?

原因は 2 つあり、どちらも設定に関係します。settings.ymlsearch: にある formats リストから json が抜けている場合があります。これはデフォルトの状態です。もう 1 つは、リミッターが有効で、スクリプトが bot と判定された場合です。まずフォーマットを追加し、docker compose restart core で再起動してから再試行します。それでも失敗する場合は limiter: false を設定し、リバースプロキシ側でアクセスを制御します。

リミッターを無効にしていても Valkey コンテナは必要ですか?

起動したままにします。SearXNG は Valkey なしでも動作しますが、Valkey がなければ後からリミッターを有効にできません。また、その他の短期間だけ使う状態も保持します。コンテナは小さく、キャッシュデータだけを保存するため、削除しても節約できる容量はごくわずかです。その一方で、後から使える選択肢を失います。

SearXNG を更新するにはどうすればよいですか?

/opt/searxngdocker compose pull を実行し、その後 docker compose up -d を実行します。Compose はイメージが変更されたコンテナを再作成し、core-config/ ディレクトリはそのまま残すため、settings.yml も保持されます。use_default_settings: true は同梱されたデフォルト値に独自のキーをマージするため、上流で追加されたオプションもファイルを壊さず、適切な値で反映されます。

複数の人で 1 つのインスタンスを共有できますか?

できます。この場合はリミッターを有効にし、public_instance: true を設定します。設定は訪問者ごとのブラウザーに保存されるため、管理するアカウントはありません。公開してから 1 週間は /stats を監視します。検索先のエンジンは、結果の欠落に気付くよりもかなり前に、サーバーからのリクエストを拒否し始めることがあるためです。