Поднять свой VPN — это не «для гиков». Весь процесс укладывается в полчаса и стоит примерно как чашка кофе в месяц. Взамен вы получаете канал, который принадлежит только вам: его не переполняют чужие пользователи, он не продаёт вашу историю посещений и его не отключат в один день вместе с очередным популярным сервисом.

В этой инструкции — весь путь по шагам: как настроить VPN на сервере с нуля, от покупки VPS до рабочего подключения на телефоне и компьютере. Мы собрали схему VLESS + WebSocket через Cloudflare Tunnel — она сложнее в настройке, чем популярный вариант с REALITY, но у неё есть решающее преимущество: она работает там, где остальные способы отваливаются, включая мобильный интернет с блокировками. Ниже объясним, почему так, и покажем разницу на реальных замерах.

Все команды в статье выполнены на живом сервере, а не переписаны из документации: ответы, ошибки и подводные камни — настоящие.

Что понадобится

  • Сервер (VPS) за пределами России — подойдёт самый дешёвый тариф, VPN почти не потребляет ресурсов.
  • Домен — любой, самый дешёвый. Нужен для туннеля Cloudflare.
  • Аккаунт Cloudflare — бесплатный, тарифа Free достаточно.
  • 30 минут времени. Опыт администрирования не нужен: все команды даны готовыми, их достаточно скопировать.

Какой сервер выбрать

Требования минимальные — 1 ядро, 1-2 ГБ оперативной памяти, любой диск. Важнее другое:

  • Локация. Чем ближе к вам физически, тем ниже задержка. Для России оптимальны Нидерланды, Германия, Финляндия, Швеция — пинг обычно 40-60 мс. США или Сингапур дадут 150-250 мс, и это заметно.
  • Операционная система. Берите Ubuntu 24.04 LTS — все команды ниже написаны под неё. Debian 12 подойдёт точно так же, команды совпадают.
  • IPv6. Убедитесь, что провайдер выдаёт IPv6-адрес — почти все выдают бесплатно. Он пригодится, об этом отдельный раздел ниже.

Шаг 1. Покупка сервера

Мы делали всё на Aeza — дальше все скриншоты оттуда. Подойдёт любой другой провайдер, порядок действий везде одинаковый.

Локация

Первым делом выбираем, где будет стоять сервер. В панели сразу показан ожидаемый пинг:

Выбор локации сервера для своего VPN: список городов с пингом и ценой

Москва и Петербург дают лучший пинг (40 и 54 мс), но для наших задач они не подходят — сервер должен стоять за пределами России. Оптимальный выбор по соотношению «скорость/цена» — Стокгольм или Хельсинки: 75 мс при той же цене. Франкфурт даст 85 мс, Амстердам 99 мс. Лондон обойдётся дороже остальных.

Тариф

Берём самый младший — для VPN его хватает с большим запасом:

Выбор минимального тарифа VPS для VPN: 1 ядро, 2 ГБ, 30 ГБ NVMe

Тариф SWEs-1: 1 ядро, 2 ГБ памяти, 30 ГБ NVMe, канал до 1 Гбит/с — 593 ₽ в месяц. Оплата возможна почасовая, 1,40 ₽ в час: удобно, если хочется сначала попробовать и не платить сразу за месяц. При оплате за три месяца дают скидку.

Операционная система

Выбор операционной системы Ubuntu 24.04 при заказе сервера для VPN

Выбираем Ubuntu 24.04 — все команды в статье написаны под неё. Debian 12 из соседнего пункта тоже подойдёт, команды совпадают.

Данные для подключения

После оплаты сервер создаётся за пару минут, и в панели появляются доступы:

Данные для подключения к серверу по SSH: IP-адрес, логин root и пароль

Нужны три вещи: IP-адрес, логин (обычно root) и пароль. Сохраните их — без них к серверу не подключиться.

IPv6 — не пропустите

На вкладке «IP-Адреса» видно, что вместе с сервером выдаётся ещё и IPv6-подсеть:

IPv6-адрес выдаётся вместе с сервером при покупке VPS

Он достаётся бесплатно, и его стоит задействовать: ниже будет отдельный раздел о том, зачем именно IPv6 нужен и какие сервисы без него не работают.

Шаг 2. Домен и Cloudflare

Домен нужен только как «вывеска» для туннеля — сайт на нём поднимать не придётся. Подойдёт любая дешёвая зона, регистратор тоже любой: мы регистрировали у того же хостера, чтобы всё было в одной панели.

Регистрация домена для VPN через Cloudflare Tunnel

Добавляем домен в Cloudflare

Регистрируемся на dash.cloudflare.com и нажимаем Add a Site:

Добавление домена в Cloudflare через кнопку Add a Site

Вводим доменное имя, жмём Continue и выбираем тариф Free — его возможностей достаточно:

Ввод домена и выбор бесплатного тарифа Free в Cloudflare

Cloudflare просканирует существующие DNS-записи и покажет их списком.

Ничего добавлять здесь не нужно. Если домен новый и список пустой — так и должно быть, смело продолжайте. Если регистратор успел создать свои записи (например, A-запись на страницу-заглушку), для VPN они не нужны — их можно удалить или просто оставить, туннелю они не мешают.

Отдельно проговорим момент, на котором новички спотыкаются: A-запись на IP вашего сервера создавать не надо. Это противоречит привычной логике «домен должен указывать на сервер», но при работе через туннель адрес сервера в DNS не публикуется вовсе — в этом и смысл. Нужную запись позже создаст сама команда cloudflared tunnel route dns, и в панели она появится с типом Tunnel и статусом Proxied. Так и должно быть, переключать её в «DNS only» нельзя — туннель перестанет работать.

Режим «DNS only» (серое облако) понадобится только в одном случае: если помимо туннеля вы держите на этом же домене что-то, работающее напрямую по IP — обычный сайт, почту или второй VPN-профиль на 443. Тогда соответствующая A-запись должна быть именно серой, иначе Cloudflare начнёт её проксировать и прямые подключения сломаются.

В конце система выдаст два адреса своих NS-серверов, вроде eloise.ns.cloudflare.com и jimmy.ns.cloudflare.com. Выпишите оба.

Меняем NS у регистратора

Заходим в панель, где куплен домен, находим настройки DNS-серверов и заменяем текущие значения на два адреса от Cloudflare:

Замена NS-серверов домена на серверы Cloudflare в панели регистратора

Две подсказки, которые сэкономят вам часы ожидания:

  • Адреса должны отличаться. Если случайно вписать один и тот же дважды, реестр домена молча не примет изменение, и вы будете смотреть на неизменившиеся записи, не понимая, почему ничего не происходит. Проверьте глазами, что первый и второй адрес разные.
  • Смена занимает от 15 минут до нескольких часов. Панель регистратора может показывать новые значения раньше, чем обновится фактическое делегирование.

Проверить реальное состояние для доменов в зоне .ru можно так:

dig NS ваш-домен.ru @a.dns.ripn.net +norecurse

Когда в ответе появятся адреса Cloudflare — готово. Дополнительно Cloudflare пришлёт письмо об активации.

Шаг 3. Подключение к серверу

Управление сервером идёт через SSH — защищённое подключение к командной строке.

Windows: откройте PowerShell (правой кнопкой по «Пуск» → Терминал) и введите, подставив свой адрес:

ssh root@203.0.113.10

macOS и Linux: та же команда в «Терминале».

Android и iOS: установите бесплатное приложение Termius, добавьте новый хост, укажите IP, логин и пароль.

При первом подключении система спросит подтверждение подлинности сервера — введите yes. Затем введите пароль. Учтите: при вводе пароля на экране ничего не отображается, даже звёздочки — это нормально. Приглашение вида root@server:~# означает, что вы внутри.

Шаг 4. Установка Xray

Xray — программа, которая будет обслуживать ваши подключения. Сначала обновляем список пакетов и ставим то, что нужно установщику:

apt update && apt install -y curl unzip

Не пропускайте эту команду. На свежем сервере список пакетов пуст, и установщик Xray падает с сообщением error: Installation of unzip failed, please check your network — при том что с сетью всё в порядке. Мы наступили на это при проверке инструкции.

Теперь сама установка:

bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install

Проверяем:

xray version

Должна появиться строка вида Xray 26.3.27 ... A unified platform for anti-censorship.

Шаг 5. Настройка Xray

Нам понадобится всего одно значение — идентификатор пользователя. Он же ваш пароль от VPN:

xray uuid

Получится строка вида 06a8ca77-f130-44f4-9773-29b88e79e893. Скопируйте её в блокнот.

Открываем конфигурацию:

nano /usr/local/etc/xray/config.json

Удаляем всё содержимое (зажать Ctrl+K, пока файл не опустеет) и вставляем, подставив свой UUID:

{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 10000,
      "protocol": "vless",
      "settings": {
        "clients": [ { "id": "ВАШ_UUID", "email": "main" } ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": { "path": "/ws-7f3a91" }
      },
      "sniffing": { "enabled": true, "destOverride": ["http", "tls"] }
    }
  ],
  "outbounds": [
    { "tag": "v4", "protocol": "freedom", "settings": { "domainStrategy": "UseIPv4v6" } },
    { "tag": "v6", "protocol": "freedom", "settings": { "domainStrategy": "UseIPv6v4" } }
  ],
  "routing": {
    "rules": [
      {
        "type": "field",
        "domain": ["domain:claude.ai", "domain:claude.com", "domain:anthropic.com"],
        "outboundTag": "v6"
      }
    ]
  }
}

Сохраняем: Ctrl+O, Enter, Ctrl+X.

Обратите внимание на три детали. Слушаем только 127.0.0.1 — наружу этот порт открывать не нужно, соединение придёт изнутри через туннель. Путь /ws-7f3a91 замените на свой произвольный, чем менее очевидный, тем лучше. Про блок с двумя выходами и правилом маршрутизации — отдельный раздел ниже, пока просто оставьте как есть.

Проверяем и запускаем:

xray -test -config /usr/local/etc/xray/config.json
systemctl restart xray
systemctl status xray

Важно: именно restart, а не enable --now. Установщик уже запустил Xray со своим пустым конфигом, поэтому команда запуска ничего не сделает — сервис покажет active (running), но ваш конфиг не применится, и VPN не заработает. Это самая коварная ошибка в такой настройке: внешне всё хорошо, а порт не слушается. Проверить можно так:

ss -ltnp | grep 10000

Если строка есть — конфиг применился.

Шаг 6. Cloudflare Tunnel

Теперь связываем сервер с сетью Cloudflare. Сервер сам установит исходящее соединение — открывать входящие порты не нужно, и реальный IP сервера нигде не публикуется.

Установка

curl -sL -o /tmp/cf.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
dpkg -i /tmp/cf.deb
cloudflared --version

Авторизация

cloudflared tunnel login

Команда выведет ссылку. Откройте её в браузере, где вы вошли в Cloudflare, и подтвердите доступ для своего домена. Сертификат сам загрузится на сервер — никаких API-ключей создавать не нужно.

Нажимайте подтверждение сразу. Если потянуть несколько минут, сессия протухает, и загрузка падает с ошибкой Failed to fetch resource при полностью исправной сети. Лечится повторным запуском команды и быстрым кликом.

Создание туннеля

cloudflared tunnel create my-vpn

В ответе будет идентификатор — длинная строка вида 202d14e6-f484-4c06-bcc0-63e96faad983. Создаём конфигурацию:

nano /root/.cloudflared/config.yml

Вставляем, подставив свой идентификатор, поддомен и путь:

tunnel: ВАШ_ИДЕНТИФИКАТОР
credentials-file: /root/.cloudflared/ВАШ_ИДЕНТИФИКАТОР.json

ingress:
  - hostname: vpn.ваш-домен.ru
    service: http://127.0.0.1:10000
  - service: http_status:404

Последняя строка обязательна: всё, кроме указанного поддомена, будет получать обычную 404.

Привязка поддомена и запуск

DNS-запись создаётся автоматически, в панель Cloudflare заходить не нужно:

cloudflared tunnel route dns my-vpn vpn.ваш-домен.ru
cloudflared service install
systemctl enable --now cloudflared
systemctl status cloudflared

Проверяем журнал:

journalctl -u cloudflared -n 20 --no-pager

Должны появиться строки Registered tunnel connection — обычно четыре, по числу соединений с ближайшими узлами Cloudflare. В них же виден город: location=fra19 означает Франкфурт.

Как это выглядит в панели Cloudflare

Зайдите в раздел DNS своего домена — там должна появиться ровно одна запись, созданная автоматически:

ИмяТипСодержимоеСтатус
vpn.ваш-домен.ruTunnelимя вашего туннеляProxied

Если запись на месте и туннель показывает active — серверная часть готова полностью. Никаких других записей для работы VPN не требуется.

Шаг 7. Подключение с устройств

Собираем ссылку по шаблону, подставив свой UUID, поддомен и путь:

vless://ВАШ_UUID@vpn.ваш-домен.ru:443?encryption=none&security=tls&sni=vpn.ваш-домен.ru&fp=safari&alpn=http%2F1.1&type=ws&host=vpn.ваш-домен.ru&path=%2Fws-7f3a91#Мой-VPN

Что здесь важно:

ПараметрЗначение
sni и hostваш поддомен, оба одинаковые
pathпуть из конфига, косая черта записывается как %2F
alpn=http/1.1обязателен, иначе клиент попробует HTTP/2 и WebSocket не поднимется
flowотсутствует — с WebSocket он не используется

Android

Нужен v2rayNG. В Google Play его может не оказаться, поэтому надёжнее скачать APK со страницы релизов проекта на GitHub. Файлов там много — выбирайте arm64-v8a, он подходит практически всем современным телефонам:

Скачивание APK v2rayNG версии arm64-v8a со страницы релизов на GitHub

После установки скопируйте свою ссылку, нажмите «+» в правом верхнем углу и выберите «Импорт из буфера обмена»:

Импорт профиля VPN из буфера обмена в приложении v2rayNG на Android

Профиль появится в списке — выберите его и нажмите круглую кнопку внизу справа. Внизу экрана появится строка вида «Успешно: соединение заняло 108 мс» с указанием страны сервера.

iPhone и iPad: Streisand, V2Box или Shadowrocket — импорт из буфера обмена аналогичный.

Windows: v2rayN со страницы релизов проекта на GitHub, импорт по Ctrl+V, затем в трее выбрать режим «Глобальный».

macOS: V2Box из App Store или Hiddify.

Шаг 8. Проверка

Подключитесь и наберите в поиске «мой ip» — Яндекс сразу покажет оба ваших адреса:

Проверка IP-адреса после подключения к своему VPN: IPv4 и IPv6 сервера

Должны отображаться адреса вашего сервера, а не домашние. Подойдёт и любой другой сервис — ifconfig.co, 2ip.ru.

Обязательно проверьте оба типа сети: сначала по Wi-Fi, затем через мобильный интернет. Именно на мобильном обычно и вылезают проблемы, если они есть.

IPv6: зачем он нужен и почему в конфиге два выхода

Вернёмся к блоку outbounds с тегами v4 и v6. Это не украшение — он решает вполне конкретную проблему.

Дело в том, что часть сервисов относится к дата-центровым IPv4-адресам с подозрением. Показательный пример — приложение Claude на Android: через обычный IPv4 сервера оно не открывается вовсе, хотя тот же сайт в браузере работает нормально. Причина в том, что Cloudflare выдаёт таким адресам JS-челлендж («Just a moment...»), браузер его молча решает, а нативное приложение — нет и упирается в ошибку.

А вот на IPv6-адрес того же сервера челленджа нет. Поэтому в конфиге сделано так: весь трафик идёт по IPv4, а домены Claude — по IPv6. Проверить можно прямо через туннель:

curl --socks5-hostname 127.0.0.1:ПОРТ https://claude.ai/cdn-cgi/trace

В ответе должен быть IPv6-адрес сервера, тогда как обычные сайты будут видеть IPv4.

Как добавить свои домены в IPv6-правило

Список доменов, идущих через IPv6, вы задаёте сами. Открываем конфиг:

nano /usr/local/etc/xray/config.json

Находим блок routing и дописываем нужные домены в массив domain через запятую:

"routing": {
  "rules": [
    {
      "type": "field",
      "domain": [
        "domain:claude.ai",
        "domain:claude.com",
        "domain:anthropic.com",
        "domain:example.com",
        "domain:another-service.net"
      ],
      "outboundTag": "v6"
    }
  ]
}

Обратите внимание на запятые: они ставятся между строками, но не после последней — это самая частая ошибка при правке, из-за неё конфиг перестаёт читаться.

Префикс перед доменом определяет, как именно он сопоставляется:

ЗаписьЧто попадает под правило
domain:example.comсам домен и все поддомены: example.com, api.example.com, cdn.example.com
full:example.comтолько точное совпадение, поддомены не затрагиваются
keyword:exampleлюбой домен, содержащий это слово

В большинстве случаев нужен именно domain: — сервисы почти всегда используют несколько поддоменов для API, картинок и авторизации, и все они должны идти одним маршрутом. Если отправить через IPv6 только основной домен, а API останется на IPv4, приложение может вести себя странно: часть запросов проходит, часть нет.

После правки обязательно проверяем синтаксис и перезапускаем:

xray -test -config /usr/local/etc/xray/config.json
systemctl restart xray

Если вместо Configuration OK появилась ошибка — почти наверняка дело в лишней или пропущенной запятой. Xray в таком случае продолжит работать со старым конфигом до перезапуска, так что связь вы не потеряете.

Как проверить, что домен пошёл через IPv6

Для сайтов за Cloudflare есть удобная служебная страница — подключитесь через VPN и откройте в браузере:

https://ваш-домен-для-проверки/cdn-cgi/trace

В строке ip= будет адрес, которым вас видит сайт. Если там IPv6-адрес вашего сервера — правило сработало. Для сайтов без Cloudflare подойдёт любой сервис проверки IP, открытый в браузере: сравните, что он показывает при обычном сёрфинге и при заходе на нужный сервис.

Ещё один способ — посмотреть на самом сервере, каким протоколом ушло соединение:

tail -f /var/log/xray/access.log

В строках соединений будет пометка [v4] или [v6] — сразу видно, какой маршрут выбран для каждого запроса. Для этого нужен включённый лог доступа, как описано в разделе про диагностику.

Пустить весь трафик через IPv6

Если хотите пустить весь трафик через IPv6, замените блок outbounds на одну строку и удалите блок routing:

"outbounds": [
  { "protocol": "freedom", "settings": { "domainStrategy": "UseIPv6v4" } }
]

Учтите побочный эффект: сайты проверки будут показывать IPv6 вместо купленного адреса, а геолокация IPv6-подсетей у провайдеров часто отличается от IPv4 — можно оказаться «в другой стране».

Если что-то не работает

Включаем подробный лог: в конфиге меняем блок log на такой, перезапускаем Xray и пробуем подключиться.

"log": {
  "loglevel": "info",
  "access": "/var/log/xray/access.log",
  "error": "/var/log/xray/error.log"
}

Смотрим, что происходит:

tail -f /var/log/xray/error.log

Основные сценарии:

Что видимПричинаРешение
в логе пусто, туннель не поднялсяпроблема с cloudflaredsystemctl status cloudflared, смотреть journalctl
подключение есть, сайты не грузятсянастройки приложениясм. ниже
работает и отваливаетсяэнергосбережение Androidсм. ниже

Проверить, что туннель жив, можно и снаружи — откройте в браузере https://vpn.ваш-домен.ru. Должна открыться страница с ошибкой 404 от Cloudflare: это значит, что домен работает, туннель поднят, а путь просто не тот — так и должно быть.

Подключается, но сайты не открываются

Почти всегда причина в приложении. Проверьте три настройки:

  • Проксирование по приложениям. Если включён режим «только выбранные приложения», а браузер в список не попал, через туннель пойдут лишь служебные запросы. Выглядит как полностью нерабочий VPN. Отключите.
  • Режим маршрутизации — поставьте глобальный.
  • Фрагментация (Fragment) — выключите. Её советуют как средство от блокировок, но здесь она только мешает.

Соединение отваливается само по себе

Если VPN работает, а потом без причины перестаёт — виновата система энергосбережения. Особенно этим грешат Xiaomi, Honor, Huawei, OPPO и Samsung: они выгружают фоновый VPN-сервис ради экономии батареи.

Разрешите приложению автозапуск и работу в фоне, снимите ограничения батареи (поставьте «Без ограничений»), а карточку приложения закрепите в списке недавних.

REALITY на 443: быстрая альтернатива, но не для всех

Если поискать инструкции по своему VPN, почти везде предлагают другую схему — VLESS + REALITY напрямую на порт 443, без домена и без Cloudflare. Она проще в настройке и быстрее в работе, потому что трафик идёт кратчайшим путём, без крюка через CDN.

Мы настроили и её на том же сервере, и вот честный результат: она заработала не на всех каналах. С одного домашнего подключения REALITY не поднялся вообще, тогда как туннель Cloudflare с того же устройства заработал сразу.

Причина — в размере первого пакета. Клиент отправляет TLS-приветствие, и его размер зависит от того, какой браузер он эмулирует. Мы замерили на живом сервере:

Эмулируемый браузерРазмер приветствия
fp=firefox1893 байта
fp=chrome1824 байта
fp=randomized517 байт

Дело в том, что современные Chrome и Firefox добавляют в приветствие постквантовый ключ, и оно раздувается почти до двух килобайт. В обычной сети это неважно. Но на каналах с уменьшенным размером сегмента — мобильный интернет, домашние подключения через туннель провайдера, корпоративные сети — такое приветствие не помещается в один пакет и разрезается на два. И вот тут начинается самое интересное: второй кусок нередко просто не доходит. Соединение зависает, а в логе сервера появляется failed to read client hello.

Помогает параметр fp=randomized: приветствие уменьшается втрое и укладывается в один пакет. Но и это не панацея — на нашем тесте после этого соединение упёрлось уже в проверку подлинности.

Схема с Cloudflare от этой проблемы избавлена в принципе: телефон устанавливает обычное TLS-соединение с ближайшим узлом CDN, никакой особой начинки в приветствии нет, и цепляться фильтрам не за что. Поэтому в этой инструкции основным вариантом выбран именно туннель, хотя он и сложнее.

Обслуживание

Добавить пользователя

Чтобы раздать доступ близким, не отдавайте свою ссылку — сделайте отдельный профиль. Генерируем новый UUID командой xray uuid и добавляем в массив clients:

"clients": [
  { "id": "ПЕРВЫЙ_UUID", "email": "main" },
  { "id": "ВТОРОЙ_UUID", "email": "mama" }
]

После systemctl restart xray профиль заработает. Ссылка собирается по тому же шаблону, меняется только UUID.

Добавить второй сервер на тот же домен

Один домен обслуживает сколько угодно серверов — каждому свой поддомен (vpn2, vpn3 и так далее). На новом сервере повторяются шаги 4-6, а туннель создаётся с другим именем и другим поддоменом.

Полезные команды

systemctl status xray          # состояние Xray
systemctl status cloudflared   # состояние туннеля
systemctl restart xray         # перезапуск после правки конфига
journalctl -u cloudflared -n 50 --no-pager

И правило, которое сэкономит часы: после каждого перезапуска сервера переподключайтесь в приложении вручную. Старые соединения обрываются, а клиент не всегда понимает это сам — со стороны выглядит как «внезапно перестало работать».

Когда свой VPN не подходит

Личный VPN отлично решает свою задачу — доступ к сайтам с вашего собственного канала. Но у него есть граница применимости.

Для любого сайта ваш трафик выглядит как трафик из дата-центра: IP принадлежит хостинг-провайдеру, и это видно по открытым базам. Обычному сёрфингу это не мешает, но там, где сайт оценивает, похож ли посетитель на обычного человека — регистрации, маркетплейсы, соцсети, реклама, — серверный адрес сразу выделяется. Дополнительно выдаёт TCP-отпечаток: по параметрам соединения пассивные детекторы отличают Linux-сервер от телефона, даже не заглядывая внутрь трафика.

Если задача именно в этом — выглядеть обычным пользователем мобильной сети, — нужен другой инструмент. Мобильные прокси работают через настоящие SIM-карты в сетях операторов: для удалённой стороны такой трафик и есть трафик мобильного абонента, потому что физически им и является. Как устроена такая инфраструктура изнутри, мы разбирали в материале про прокси-ферму.

Оба подхода прекрасно уживаются: свой VPN — для личного доступа к сайтам и защиты в публичных сетях, мобильные прокси — для задач, где важно, как вас видит удалённая сторона.