SSD Nodes Learn 🎉 VPS від $5.50/міс
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-13

Як розмістити .onion-сайт на VPS

Налаштуйте v3 onion-сервіс на Ubuntu: tor і nginx працюють через loopback, а перевірки допомагають не розкрити зв’язок адреси з публічною IP-адресою.

Що ви налаштовуєте

Onion-сайт — це звичайний вебсервер, який відповідає на запити лише через мережу Tor. Установіть tor, додайте два рядки до /etc/tor/torrc, прочитайте адресу, яку tor запише для вас, а потім прив’яжіть nginx до 127.0.0.1, щоб сервер не відповідав на публічній IP-адресі. Установлення займає десять хвилин. Решта цього посібника присвячена переліку можливих витоків, оскільки найчастіше onion-сайт працює неправильно через те, що його власна конфігурація безпосередньо вказує на оператора.

Tor спочатку називався «цибулевим маршрутизатором», а onion-сервіс — це сервіс, доступний лише через Tor. Адреса версії 3 містить 56 символів, за якими йде .onion. Ці символи — відкритий ключ сервісу ed25519, контрольна сума та байт версії, закодовані в base32. Адреси версії 2, що містили 16 символів, вилучили з мережі у 2021 році, тому все, що ви створюєте сьогодні, має версію v3. Адреса і є ключем, і це має два наслідки. З’єднання шифрується та автентифікується наскрізно без участі центру сертифікації, а втрата файлу ключа означає остаточну втрату адреси.

Ваш сервер ніколи не приймає вхідних з’єднань. Tor вибирає кілька ретрансляторів як точки знайомства, завантажує підписаний дескриптор на сервери каталогів, а потім з’єднує кожного відвідувача через ретранслятор rendezvous, який вибрав відвідувач. Усі ці з’єднання є вихідними з вашого сервера. Не потрібно відкривати порт або публікувати запис DNS.

Встановлення tor зі сховища Tor Project

Ubuntu постачає пакет tor у компоненті universe, але зазвичай він залишається близьким до версії, актуальної на момент заморожування релізу. Власне сховище 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 має містити codename вашої Ubuntu. Зчитайте його з /etc/os-release замість ручного введення. Неправильний codename дає сховище, яке коректно визначається, але не містить пакетів для вашого релізу.

. /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 підтримує ключ підпису в актуальному стані, тому ротація ключа не порушить роботу apt update через рік. Перевірте, що tor запустився та підключився до мережі:

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

Журнал має завершуватися повідомленням Bootstrapped 100% (done): Done. Якщо tor застряг на стані Bootstrapped 10%, він не має вихідного маршруту. Перевірте мережевий firewall вашого провайдера та власні правила вихідного трафіку: sudo ufw status verbose має показувати allow (outgoing) як маршрут за замовчуванням.

Надалі важливі дві назви. Пакет запускає tor від імені користувача debian-tor, а запущений unit має назву tor@default.service, оскільки tor.service у Debian та Ubuntu є оболонкою над екземпляром. Запитуйте стан і журнали за назвою екземпляра, щоб завжди отримувати інформацію про фактичний процес.

Налаштуйте onion service у torrc

Додайте два рядки в /etc/tor/torrc:

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

HiddenServiceDir — це каталог, у якому tor зберігає ключі й адресу цього сервісу. Не створюйте його вручну. Під час запуску Tor створює його з потрібними власником і режимом. Каталог, створений від імені root, спричинить першу помилку зі списку нижче.

HiddenServicePort має дві частини, і найпоширеніша перша помилка — переплутати їх. Перше число — це порт, до якого відвідувач підключається всередині тунелю. Тому 80 — очікуване значення, і змінювати його немає потреби. Друга частина — локальна адреса, на яку tor переспрямовує цей трафік. Якщо вказати лише HiddenServicePort 80, трафік буде переспрямовано на 127.0.0.1:80. Тому явне зазначення адреси та використання порту з великим номером не дає onion vhost конфліктувати з тим, що вже прослуховує порт 80.

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

У каталозі мають бути hostname, hs_ed25519_public_key, hs_ed25519_secret_key і порожній каталог authorized_clients.

Прочитайте свою адресу .onion

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

У відповідь буде один рядок: 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

Тепер перевірте з сервера дві речі. По-перше, nginx відповідає для onion-імені. Саме такий заголовок Host надішле tor:

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

По-друге, nginx відповідає на цій адресі й більше ніде:

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 надає перевагу unix socket замість loopback-порту: HiddenServicePort 80 unix:/var/run/tor/onion_site.sock, а nginx має слухати цей шлях. До socket не можна підключитися з іншого хоста, навіть якщо згодом на сервері з’явиться другий інтерфейс. Недолік полягає в правах доступу до файлів: nginx створює socket, а tor підключається до нього як debian-tor, тому обидва користувачі мають мати узгоджені права на каталог. Loopback із перевіреним результатом ss налаштувати простіше, тому далі в цьому посібнику використовується саме цей варіант.

Якщо сайт працює через loopback, для нього не потрібне жодне вхідне правило. Залиште відкритим порт 22 для себе, а решту забороніть (типові параметри ufw для VPS). Пам’ятайте: firewall не скасовує дію сервісу, прив’язаного до 0.0.0.0. Він лише фільтрує пакети, що доходять до firewall. Для контейнерів це особливо важливо, оскільки публікація Docker-порту створює правила iptables перед правилами ufw, тому -p 8080:80 робить ваш onion backend доступним через публічну IP-адресу, хоча ufw і далі показує порт як заборонений. Публікуйте порти контейнерів як -p 127.0.0.1:8080:80.

Витоки, які деанонімізують onion-сайт

Tor приховує розташування сервера. Але Tor не приховує дані, які сервер передає. Кожен наведений нижче елемент публікує ваш власний стек.

Той самий сайт, доступний за вашою публічною IP-адресою

Саме на цьому найчастіше помиляються. Сканери постійно індексують HTTP-відповідь кожної доступної з мережі адреси, а результати є публічними та доступними для пошуку. Якщо передавати ту саму сторінку за вашою публічною IP-адресою та onion-адресою, об’єднати їх можна одним запитом: однаковий заголовок, однаковий хеш 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 на публічному listener, щоб невідповідний заголовок Host ніколи не передавав запит до onion-vhost.

Банери версій

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

Nginx за замовчуванням відповідає Server: nginx/1.24.0. Цей рядок із версією разом із точним порядком інших заголовків утворює fingerprint, за яким onion-сайт можна зіставити з вашим clearnet-хостом. server_tokens off; скорочує його до Server: nginx. Це не видаляє заголовок. У nginx немає вбудованої директиви для його видалення, тому зазвичай використовують модуль headers-more, якщо заголовок потрібно прибрати. PHP додає X-Powered-By, доки ви не встановите expose_php = Off. etag off; також потрібно додати до цього списку, оскільки nginx формує ETag на основі часу зміни та розміру файлу. Тому однакові файли, скопійовані на два сервери, матимуть однаковий ETag на обох.

Абсолютні URL, що вказують на ваш clearnet-домен

Тег rel="canonical", Open Graph og:url, RSS-стрічка, sitemap, email для скидання пароля або жорстко заданий URL логотипу. Будь-який із них вказує на clearnet-сайт усередині сторінки, переданої через onion. Використовуйте шляхи від кореня, наприклад /static/logo.svg, і нехай застосунок отримує базовий URL із host у запиті, а не з константи. Редиректи створюють ту саму проблему в іншому місці: return 301 https://example.com$request_uri; у catch-all-блоці перенаправляє відвідувача onion-сайту на ваш реальний домен, а заголовок Location одразу розкриває цю відповідь.

TLS-сертифікат, спільний із clearnet-сайтом

Onion-адреса автентифікує себе, оскільки адреса є публічним ключем. Тому http:// через onion-з’єднання вже зашифровано наскрізно, а Tor Browser розглядає його як безпечний контекст. Встановлення на onion-vhost вашого наявного сертифіката розкриває зв’язок між двома сайтами, оскільки кожен публічно довірений сертифікат записується в журнали Certificate Transparency, а ці журнали є публічними, постійними та доступними для пошуку за іменем. Залиште сертифікати Let's Encrypt на clearnet-vhost і використовуйте для onion-vhost звичайний HTTP.

Сторонні шрифти та аналітика

Шрифт із CDN (мережі доставки вмісту) або скрипт аналітики. Браузер відвідувача отримує кожен із них безпосередньо, тому сторонній сервіс дізнається, що хтось завантажив вашу сторінку, і зазвичай також яку саме сторінку. Крім того, суворіші рівні безпеки Tor Browser блокують такі запити, через що макет сторінки може відображатися неправильно. Розміщуйте локально всі ресурси, потрібні сторінці.

Невідповідність заголовка Host

Якщо server_name не збігається із заголовком Host, який надсилає tor, nginx передає запит до default server для цієї listen-адреси. На сервері з одним vhost це непомітно, оскільки єдиний server block одночасно є default. Якщо пізніше додати clearnet-vhost, onion-запити можуть почати потрапляти до нього разом із його canonical-тегами та редиректами. Повторюйте перевірку curl -H 'Host: ...' після кожної зміни nginx і виконуйте grep результату для пошуку вашого реального домену:

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

Знання про те, який процес володіє яким socket, є значною частиною цієї роботи (як працюють порти та listening sockets у Linux).

Що залишається в журналах

Кожен запит надходить із 127.0.0.1, тому nginx не має адреси відвідувача для запису, а access_log off; нічого вам не коштує. Застосунок над ним — інша справа: ви відповідаєте за обробку замовлення, email-адреси або метаданих завантаженого файлу. Важливі й ваші власні звички: адміністрування сервера через незахищений login не належить до того, що захищає Tor. Тому вважайте захист SSH на тому самому VPS частиною цієї конфігурації.

Створіть резервну копію приватного ключа, оскільки він є адресою

/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

Та сама адреса з’явиться на новому обладнанні через 1–2 хвилини після того, як tor повторно опублікує дескриптор. На цьому міграцію завершено: змінювати DNS або повторно випускати сертифікат не потрібно.

Onion-Location, якщо сайт також доступний у clearnet

Якщо onion-адреса потрібна для зручності, а не для приховування сайту, повідомте її в clearnet vhost:

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

Після цього Tor Browser показує кнопку .onion available в адресному рядку та пропонує перейти на onion-адресу. Заголовок враховується лише тоді, коли сторінка clearnet доступна через HTTPS, а його значення є дійсною onion URL.

Тут є важливий нюанс nginx. Директиви add_header успадковуються блоком location лише тоді, коли цей блок не містить власних директив, тому location із власним add_header непомітно втрачає Onion-Location. Повторіть директиву в цьому блоці або зберігайте всі заголовки відповіді в одному місці. Публікація цього заголовка навмисно пов’язує два сайти. Це правильно для дзеркала, але неправильно для ресурсів, які мають залишатися непов’язаними.

Власні адреси

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/, де містяться hostname і hs_ed25519_secret_key. Щоб установити такий результат, зупиніть tor, скопіюйте цей каталог у HiddenServiceDir, а потім застосуйте ті самі chown і chmod 700, що й під час описаної вище процедури відновлення.

Довжина префікса визначає всі витрати. Адреса має кодування base32, тому кожен додатковий потрібний символ збільшує очікувану кількість ключів у 32 рази. Короткий префікс можна отримати на ноутбуці. Для довгого префікса не вистачить жодного доступного вам обладнання. Власний префікс також привчає читачів розпізнавати лише перші кілька символів, а не всю адресу. Саме на цій звичці будуються фішингові копії 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). Клієнт не зміг отримати дескриптор, тому з погляду мережі за цією адресою нічого не опубліковано. Переконайтеся, що tor запущений і завершив початкове підключення, порівняйте введену адресу з sudo cat /var/lib/tor/onion_site/hostname посимвольно, а потім перевірте системний час. Tor потребує точного часу, щоб публікувати та перевіряти дескриптори, а timedatectl має повідомити System clock synchronized: yes.

Адреса визначається, але сторінка не завантажується. Tor встановив rendezvous-з’єднання, а потім не зміг пройти останню ділянку — від tor до nginx. Ця ділянка локальна, тому в журналі tor не буде повідомлення. Виконайте curl -sI http://127.0.0.1:8080/ на сервері. Connection refused означає, що nginx не запущений або прослуховує іншу адресу, ніж та, на яку вказує HiddenServicePort.

Сторінка завантажується, але кожне посилання веде на ваш реальний домен. У шаблонах використовуються абсолютні URL. Виконайте наведену вище перевірку grep -o 'https\?://[^"]*' і виправте все, що вона виведе, перш ніж поширювати адресу.

Сервіс працює, але припиняє роботу після перезавантаження. Навмисно перезавантажте сервер один раз, перш ніж покладатися на цей сайт, а потім виконайте sudo systemctl status tor@default і sudo systemctl status nginx. Сервіс, запущений вручну, до перезавантаження виглядає так само, як увімкнений для автозапуску.

FAQ

Чи потрібно відкривати порт у firewall для onion-сервісу Tor?

Ні. Демон tor встановлює лише вихідні з’єднання з directory servers, introduction points і кожним rendezvous relay, тому правило для вхідних з’єднань не потрібне, а сам вебсервер прослуховує 127.0.0.1. Залиште для ufw стандартну заборону вхідного трафіку, дозволивши SSH. З тієї самої причини onion-сервіс працює на машині за NAT (network address translation), навіть якщо вона взагалі не має публічної IP-адреси.

Чому я не можу відкрити свою адресу .onion у Tor Browser?

Перевіряйте проблему від сервера назовні. sudo journalctl -u tor@default -n 50 має показати Bootstrapped 100% (done): Done, після цього curl -sI http://127.0.0.1:8080/ на сервері має повернути рядок зі статусом. Потім порівняйте введену адресу з файлом hostname, оскільки навіть один неправильний символ означає інший сервіс. Onionsite Not Found (0xF0) означає, що для цієї адреси не знайдено descriptor. Зазвичай це означає, що tor не запущений або системний час неправильний.

Чи можна перенести onion-сайт на новий сервер і зберегти ту саму адресу?

Так. Адреса обчислюється з hs_ed25519_secret_key, тому скопіюйте весь HiddenServiceDir на новий сервер, призначте йому власника debian-tor і режим 700, а потім запустіть tor. Адреса знову стане доступною після повторної публікації descriptor. Оновлювати DNS-запис не потрібно. Якщо втратити цей файл, адресу неможливо відновити. Тому в день його створення зробіть зашифровану резервну копію та збережіть її поза сервером.

Чи потрібен onion-сайту сертифікат HTTPS?

Ні. 56-символьна адреса є публічним ключем сервісу, тому з’єднання вже зашифроване й автентифіковане від кінця до кінця, а Tor Browser розглядає http:// у домені .onion як захищений контекст. Повторне використання clearnet-сертифіката для onion vhost гірше, ніж повна відмова від сертифіката, оскільки журнали Certificate Transparency є публічними та назавжди фіксують, які імена використовують один сертифікат. Єдина причина придбати сертифікат для домену .onion — підтвердження бренду від CA, який видає такі сертифікати. Це посилання є публічним за задумом.