SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-13

VPSで.onionサイトを構築する方法

Ubuntuでtorとnginxを使い、loopbackにバインドしたv3 onion serviceを構築します。公開IPへ戻る情報漏えいを防ぐ設定と、現在のTor Projectリポジトリの注意点も確認できます。

構築するもの

onion サイトは、Tor ネットワーク経由でのみ応答する通常の Web サーバーです。tor をインストールし、/etc/tor/torrc に 2 行を追加して、tor が出力したアドレスを確認します。その後、nginx を 127.0.0.1 にバインドし、パブリック IP では何も応答しないようにします。インストールには 10 分かかりません。このガイドの大部分は、情報漏えいにつながる設定の一覧です。onion サイトでよくある失敗は、サイト自身の設定が運用者の環境へ直接戻るよう指定してしまうことです。

Tor は当初「the onion router」として始まり、onion service は Tor 経由でのみ到達できるサービスです。Version 3 のアドレスは 56 文字に .onion が続く形式です。これらの文字は、サービスの ed25519 公開鍵、チェックサム、バージョンバイトを base32 でエンコードしたものです。Version 2 のアドレス(16 文字)は 2021 年にネットワークから削除されたため、現在生成されるものはすべて v3 です。アドレス自体が鍵です。そのため、2 つの点が重要になります。接続は認証局を介さず、エンドツーエンドで暗号化および認証されます。また、鍵ファイルを失うと、そのアドレスを完全に失います。

サーバーが受信接続を受け付けることはありません。Tor は複数のリレーを introduction point として選び、署名済みの descriptor を directory server にアップロードします。その後、訪問者が選んだ rendezvous relay で各訪問者と接続します。これらの接続はすべて、サーバーから外向きに確立されます。開放すべきポートはなく、公開すべき DNS レコードもありません。

Tor Project リポジトリから tor をインストールする

Ubuntu には universe の tor パッケージが含まれていますが、リリースの凍結時点でのバージョンに近いままです。Tor Project 独自のリポジトリでは現行の安定版を追跡しています。アドレスの匿名性を維持するかどうかを決めるソフトウェアには、こちらを使用します。

sudo apt update
sudo apt install -y apt-transport-https gnupg wget
KEYURL=https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
wget -qO- "$KEYURL" | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null

リポジトリのエントリには deb822 形式を使用し、Suites には Ubuntu のコードネームを指定する必要があります。手入力せず、/etc/os-release から読み取ってください。コードネームを間違えると、リポジトリの解決自体は正常に見えても、そのリリース用のパッケージが存在しなくなります。

. /etc/os-release
sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null <<EOF
Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: $VERSION_CODENAME
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
EOF
sudo apt update
sudo apt install -y tor deb.torproject.org-keyring

deb.torproject.org-keyring パッケージによって署名鍵が最新に保たれるため、鍵のローテーションが発生しても、1 年後に apt update が壊れることはありません。tor が起動し、ネットワークに接続できたことを確認します。

tor --version
sudo journalctl -u tor@default -n 20

journal の末尾は Bootstrapped 100% (done): Done になるはずです。tor が Bootstrapped 10% で停止している場合、外向きの経路がありません。プロバイダー側のネットワークファイアウォールと、自身の egress ルールを確認してください。デフォルト経路として sudo ufw status verboseallow (outgoing) を示している必要があります。

ここからは、2 つの名前が重要です。パッケージは debian-tor ユーザーとして tor を実行します。また、実行中の unit は tor@default.service です。tor.service は Debian と Ubuntu でインスタンスをラップする仕組みだからです。インスタンス名を指定して状態とログを確認すれば、常に実際のプロセスを確認できます。

torrc で onion service を設定する

/etc/tor/torrc に次の 2 行を追加します。

HiddenServiceDir /var/lib/tor/onion_site/
HiddenServicePort 80 127.0.0.1:8080

HiddenServiceDir には、このサービスの鍵とアドレスが保存されます。自分で作成しないでください。Tor は起動時に、必要な所有者と mode でこのディレクトリを作成します。root で作成したディレクトリは、下記の失敗一覧にある最初のエラーの原因になります。

HiddenServicePort には 2 つの部分があり、これを取り違えることが最初によくある間違いです。最初の番号は、訪問者がトンネル内で接続するポートです。そのため、80 は一般的な設定であり、変更する理由はありません。2 番目の部分は、Tor がそのトラフィックを転送するローカルアドレスです。単独の HiddenServicePort 80127.0.0.1:80 に転送します。アドレスを明示し、高い番号のポートを使用すると、onion vhost が 80 番ポートですでに待ち受けているサービスと競合しません。

sudo systemctl restart tor@default
sudo ls -l /var/lib/tor/onion_site/

一覧には hostnamehs_ed25519_public_keyhs_ed25519_secret_key と、空の authorized_clients ディレクトリが含まれているはずです。

.onion アドレスを確認する

sudo cat /var/lib/tor/onion_site/hostname

1 行が返されます。56 文字の base32 文字列と .onion です。この文字列がサイト全体の識別子です。誰かが割り当てるものではなく、譲渡することもできません。鍵ファイルを保持している限り、他者に奪われることもありません。今すぐコピーしてください。以下のすべての設定で必要になります。このガイドでは、以降この文字列を <your-address>.onion と表記します。

nginx から 127.0.0.1 にバインドしてサイトを提供する

sudo apt install -y nginx
sudo install -d -m 755 /srv/onion

/etc/nginx/sites-available/onion を記述します。

server {
    listen 127.0.0.1:8080;
    server_name <your-address>.onion;

    root /srv/onion;
    index index.html;

    server_tokens off;
    etag off;
    access_log off;
    error_log /var/log/nginx/onion.error.log error;
}
echo '<h1>hello from the onion</h1>' | sudo tee /srv/onion/index.html
sudo ln -s /etc/nginx/sites-available/onion /etc/nginx/sites-enabled/onion
sudo nginx -t
sudo systemctl reload nginx

次に、サーバー上で 2 つの点を確認します。1 つ目は、nginx が onion 名に応答することです。これは tor が送信する正確な Host ヘッダーです。

curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/

2 つ目は、そのアドレスだけで応答し、それ以外では応答しないことです。

sudo ss -tlnp | grep 8080

アドレス列は 127.0.0.1:8080 になっていなければなりません。0.0.0.0:8080 または *:8080 と表示される場合、onion サイトもパブリックインターネット上で公開されています。これは漏えいリストの最初の項目です。アドレスのない listen 8080; 行は、すべてのインターフェースにバインドします。これがデフォルトです。

Tor Browser でそのアドレスを開きます。初回の読み込みには数秒かかります。クライアントが descriptor を取得し、rendezvous circuit を構築するためです。

Tor Project の公式ドキュメントでは、loopback ポートより unix socket が推奨されています。HiddenServicePort 80 unix:/var/run/tor/onion_site.sock とし、nginx はそのパスをリッスンします。socket は、サーバーに 2 つ目のインターフェースを追加した後でも、別のホストから到達できません。代わりに、ファイル権限の管理が必要です。nginx が socket を作成し、tor は debian-tor として接続するため、2 つのユーザーがディレクトリの権限を一致させる必要があります。確認済みの ss 出力を使う loopback のほうが設定しやすいため、このガイドの以降の手順ではこちらを使用します。

サイトを loopback 上で動かす場合、このサイト用の受信ルールは不要です。自分用に 22 を開け、残りは拒否します(VPS で設定しておくべき ufw のデフォルト)。ファイアウォールは 0.0.0.0 にバインドするサービスを無効化しません。ファイアウォールに到達したパケットをフィルタリングするだけです。コンテナではこの点がより重要です。Docker ポートを公開すると ufw より先に iptables ルールが書き込まれるため、-p 8080:80 によって onion バックエンドがパブリック IP 上で公開される一方、ufw ではそのポートが拒否されているように表示されます。コンテナのポートは -p 127.0.0.1:8080:80 として公開します。

匿名性を損なう onion サイトの情報漏えい

Tor はサーバーの場所を隠します。Tor はサーバーが返す内容を隠しません。以下はすべて、実行環境自体が公開する情報です。

パブリック IP でも同じサイトが応答する

これは見落とされやすい問題です。スキャナーは、到達可能なすべてのアドレスの HTTP 応答を継続的にインデックス化しており、その結果は公開され、検索できます。パブリック IP と onion アドレスで同じページを配信すると、同一性の照合に必要なのは 1 回の検索だけです。タイトル、favicon のハッシュ、ETag、ヘッダーの順序が一致します。上記の listen 127.0.0.1:8080; 行が対策です。サーバー上ではなく、別のマシンから確認してください。

curl -sv --max-time 5 http://<your-public-ip>:8080/

Connection refused またはタイムアウトが正しい結果です。HTML が返る場合、サイトは公開されています。サーバーで clearnet サイトも運用している場合は、その vhost に専用の root を割り当て、パブリックリスナーに明示的な default_server ブロックを残してください。これにより、一致しない Host ヘッダーが onion vhost にフォールスルーすることを防げます。

バージョンバナー

curl -sI http://127.0.0.1:8080/ | grep -i '^server'

デフォルトの nginx は Server: nginx/1.24.0 を返します。このバージョン文字列は、他のヘッダーの正確な順序と組み合わさることで、onion サイトと clearnet ホストを照合する指紋になります。server_tokens off; により、これを Server: nginx まで縮小できます。ヘッダー自体は削除されません。nginx には削除用の組み込みディレクティブがないため、削除したい場合は通常 headers-more モジュールを使用します。PHP は expose_php = Off を設定するまで X-Powered-By を追加します。etag off; も同じ一覧に含めてください。nginx はファイルの変更時刻とサイズから ETag を生成するため、同じファイルを 2 台のサーバーにコピーすると、両方で同じ ETag が返されます。

clearnet ドメインを指す絶対 URL

rel="canonical" タグ、Open Graph の og:url、RSS フィード、サイトマップ、パスワードリセットメール、ハードコードされたロゴ URL などがあります。いずれか 1 つでも、onion 経由で配信されたページ内に clearnet サイトの名前を埋め込みます。/static/logo.svg のような root 相対パスを使用し、アプリケーションには固定値ではなくリクエストの Host からベース URL を読み取らせてください。リダイレクトも、別の場所で発生する同じ問題です。キャッチオールブロックの return 301 https://example.com$request_uri; は onion の訪問者を実際のドメインへ送信し、Location ヘッダーはその答えを直接返します。

clearnet サイトと共有する TLS 証明書

onion アドレスでは、そのアドレス自体が公開鍵であるため、アドレスが自身を認証します。そのため、onion 接続での http:// はすでにエンドツーエンドで暗号化されており、Tor Browser は安全なコンテキストとして扱います。既存の証明書を onion vhost にインストールすると、2 つのサイトの関連性が公開されます。公開認証局が信頼する証明書はすべて Certificate Transparency ログに記録され、そのログは公開され、永続的に保存され、名前で検索できます。clearnet vhost での Let's Encrypt 証明書を使用し、onion vhost は通常の HTTP のままにしてください。

サードパーティのフォントとアナリティクス

CDN(content delivery network)から取得するフォントや、アナリティクス用スクリプトが該当します。訪問者のブラウザーはそれぞれを直接取得するため、サードパーティは誰かがページを読み込んだことと、通常はどのページを読み込んだかを把握できます。また、Tor Browser のより厳格なセキュリティレベルではリクエストがブロックされるため、レイアウトが崩れます。ページに必要なすべてのアセットを自分でホストしてください。

Host ヘッダーの不一致

server_name が tor の送信する Host ヘッダーと一致しない場合、nginx はその listen アドレスのデフォルトサーバーにフォールバックします。vhost が 1 つだけのサーバーでは、その server ブロックがデフォルトでもあるため、この問題は見えません。後から clearnet vhost を追加すると、onion リクエストがその vhost に到達し、正規 URL のタグやリダイレクトまで返す可能性があります。nginx を変更するたびに curl -H 'Host: ...' の確認を再実行し、結果に実際のドメインが含まれていないか grep してください。

curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/ | grep -o 'https\?://[^"]*' | sort -u

どのプロセスがどのソケットを所有しているかを把握することが、この作業の大部分を占めます(Linux でのポートとリスニングソケットの仕組み)。

ログに残る情報

すべてのリクエストは 127.0.0.1 から到着するため、nginx に記録できる訪問者アドレスはなく、access_log off; による負担もありません。その上で動作するアプリケーションは別の問題です。注文情報、メールアドレス、アップロードされたファイルのメタデータなどは、アプリケーション側で適切に扱う必要があります。自身の運用習慣も関係します。強化されていないログイン経由でサーバーを管理することは、Tor が保護する範囲外です。そのため、同じ VPS での SSH の強化もこの構成の一部として扱ってください。

秘密鍵をバックアップします。秘密鍵がアドレスそのものだからです。

/var/lib/tor/onion_site/hs_ed25519_secret_key がサービスです。レジストラも復旧手段もありません。秘密鍵を失うと、アドレスも失われます。コピーを作ると、そのコピーを持つ人があなたのアドレスで独自のコンテンツを提供できます。あなたが何かを失効させる方法はありません。

sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -czf onion-keys.tgz onion_site
sudo chmod 600 onion-keys.tgz
sudo systemctl start tor@default

そのアーカイブ(gpg -c onion-keys.tgz)を暗号化し、サーバーの外部へ移します。新しい VPS への復元に必要なのは、アーカイブと、所有権に関して tor が想定する内容です。

sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -xzf onion-keys.tgz
sudo chown -R debian-tor:debian-tor /var/lib/tor/onion_site
sudo chmod 700 /var/lib/tor/onion_site
sudo systemctl start tor@default
sudo cat /var/lib/tor/onion_site/hostname

tor が descriptor を再公開すると、1〜2 分後には新しいハードウェアで同じアドレスが戻ります。これが移行のすべてです。DNS の変更も証明書の再発行も必要ありません。

Onion-Location(サイトが clearnet にも存在する場合)

onion が秘密ではなく利便性のためのものであれば、clearnet の vhost から通知します。

add_header Onion-Location http://<your-address>.onion$request_uri;

Tor Browser はアドレスバーに .onion available ボタンを表示し、切り替えを提案します。このヘッダーが有効になるのは、clearnet のページが HTTPS で配信され、値が有効な onion URL である場合だけです。

ここでは nginx のルールに注意が必要です。add_header ディレクティブは、location ブロック側で独自のディレクティブを1つも宣言していない場合に限り、そのブロックへ継承されます。そのため、独自の add_header を持つ location では Onion-Location が暗黙に失われます。そこでも同じ設定を繰り返すか、すべてのレスポンスヘッダーを1か所にまとめてください。このヘッダーを意図的に公開すると、2つのサイトが関連付けられます。ミラーには適していますが、関連付けないことを目的とするサイトには適していません。

Vanity アドレス

mkp224oは、指定したプレフィックスで始まるアドレスが生成されるまでキーペアを作成します。これは総当たり検索です。そのため、設定できるのはプレフィックスと、待機できる時間だけです。

sudo apt install -y git gcc libc6-dev libsodium-dev make autoconf
git clone https://github.com/cathugger/mkp224o
cd mkp224o
./autogen.sh
./configure --enable-amd64-51-30k
make
./mkp224o -d onionkeys blog

見つかったキーペアはすべて、onionkeys/<address>.onion/内のhostnamehs_ed25519_secret_keyに保存されます。インストールするには、torを停止し、そのディレクトリをHiddenServiceDirにコピーしてから、上記の復元手順と同じchownおよびchmod 700を適用します。

コストを決めるのはプレフィックスの長さです。アドレスはbase32で表されるため、要求する文字が1文字増えるごとに、必要なキーペア数の期待値は32倍になります。短いプレフィックスならノートパソコンでも完了します。長いプレフィックスは、所有しているどのマシンでも完了しません。Vanityプレフィックスを使うと、読者はアドレス全体ではなく先頭の数文字だけを確認するようになります。この習慣は、onionサイトのフィッシング用コピーが悪用するものです。

再起動後に表示される障害と確認すべき文字列

再起動後に hostname ファイルがない。 Tor が起動していないか、ディレクトリを拒否しています。sudo journalctl -u tor@default -n 50 で確認できます。

/var/lib/tor/onion_site/ is not owned by this user (debian-tor, 108) but by root (0). Perhaps you are running Tor as the wrong user?

これは手動で作成したディレクトリに見られる状態です。所有者とモードを修正するか、ディレクトリを削除して tor に作成させてください。

Tor Browser に Onionsite Not Found (0xF0) と表示される。 クライアントが descriptor を取得できていないため、ネットワークから見ると、そのアドレスには何も公開されていません。tor が実行中で bootstrap 済みであることを確認します。入力したアドレスと sudo cat /var/lib/tor/onion_site/hostname を 1 文字ずつ比較し、その後で時刻を確認してください。Tor は descriptor の公開と検証に正確な時刻を必要とします。timedatectlSystem clock synchronized: yes を報告する必要があります。

アドレスは解決されるが、ページが読み込まれない。 Tor は rendezvous を完了した後、最後のホップで失敗しています。これは tor から nginx へのローカルな通信なので、tor のログには何も残りません。サーバー上で curl -sI http://127.0.0.1:8080/ を実行してください。Connection refused は nginx が停止しているか、HiddenServicePort が指しているアドレスとは異なるアドレスで待ち受けていることを示します。

ページは読み込まれるが、すべてのリンクが実際のドメインに移動する。 原因はテンプレート内の絶対 URL です。上記の grep -o 'https\?://[^"]*' チェックを実行し、表示された内容を修正してからアドレスを共有してください。

動作するが、再起動後に停止する。 サイトを運用する前に、意図的に 1 回再起動してください。その後、sudo systemctl status tor@defaultsudo systemctl status nginx を実行します。手動で起動したサービスは、マシンを再起動するまで、有効化されたサービスと同じように見えます。

FAQ

Tor onion service 用にファイアウォールでポートを開く必要はありますか?

いいえ。tor デーモンが行うのは、ディレクトリサーバー、introduction point、各 rendezvous relay への外向き接続だけです。そのため、受信ルールは必要ありません。Web サーバー自体は 127.0.0.1 で待ち受けます。SSH だけを許可し、受信トラフィックに対する ufw のデフォルト設定は deny のままにしてください。この特性により、onion service はパブリック IP がない NAT(network address translation)配下のマシンでも動作します。

Tor Browser で .onion アドレスに接続できないのはなぜですか?

サーバー側から順に確認します。sudo journalctl -u tor@default -n 50Bootstrapped 100% (done): Done が表示されることを確認し、次にサーバー上で curl -sI http://127.0.0.1:8080/ を実行してステータス行が返ることを確認します。その後、入力したアドレスと hostname ファイルの内容を比較してください。1 文字でも違えば、別のサービスになります。Onionsite Not Found (0xF0) は、そのアドレスの descriptor が見つからないことを示します。通常は tor が実行されていないか、システム時刻が正しくありません。

onion site を新しいサーバーへ移行して、同じアドレスを維持できますか?

はい。アドレスは hs_ed25519_secret_key から生成されます。そのため、HiddenServiceDir 全体を新しいサーバーへコピーし、debian-tor の所有者と mode 700 を設定してから tor を起動してください。descriptor が再公開されると、アドレスは再び利用可能になります。更新が必要な DNS レコードはありません。このファイルを失うと、アドレスは復元できません。作成した日に、暗号化したバックアップをサーバー外に保存してください。

onion site に HTTPS 証明書は必要ですか?

いいえ。56 文字のアドレスはサービスの公開鍵です。そのため、接続はすでにエンドツーエンドで暗号化および認証されています。また、Tor Browser は .onion name 上の http:// を secure context として扱います。clearnet の証明書を onion vhost で再利用するのは、何もしないより悪い対応です。Certificate Transparency ログは公開されており、どの名前が同じ証明書を共有しているかを恒久的に記録するためです。.onion name 用の証明書を購入する唯一の理由は、それを発行する CA によるブランド保証です。その関連付けは、意図的に公開されます。