Ваш локальный веб-сервер должен быть доступен из публичного интернета - Next.js-приложение, которое хочется показать клиенту, приёмник вебхуков на машине за CGNAT, Home Assistant, Raspberry Pi. Два ответа по умолчанию - ngrok и Cloudflare Tunnel, и оба работают отлично, пока вам не понадобятся свой домен, свои неймсерверы или лимит трафика, который вы устанавливаете сами, а не тот, что выбрал вендор. Тему не даёт умереть и хабр-синдикат Hacker News: сообщество регулярно возвращается к приёму «self-hosted HTTP-туннели на SSH и nginx» - шаблон реальный и стоит того, чтобы держать его в арсенале.[12]
Идея в одну строку: вместо того чтобы платить ngrok за релей трафика и получать URL на *.ngrok.io, вы направляете локальную машину на дешёвый VPS, которым уже управляете, и VPS завершает публичный трафик и пересылает его обратно по туннелю. Ни одна третья сторона не видит ваши байты, вендор не решает ваш месячный бюджет запросов, а hostname - ваш.
Это сравнение плюс рабочие конфиги, а не бенчмарк-пост. Я не публикую цифры производительности, которых сам не замерял, и здесь выдумывать их не буду - относитесь ко всем заявлениям о пропускной способности ниже как к «так пишут в доках самого инструмента» и выбирайте размер машины под свой реальный трафик.
Когда self-hosted-туннель действительно выигрывает
Managed-туннели - честно хорошая сделка для всплесков: поднял, расшарь, убрал. Аргументы в пользу того, чтобы забрать релей на свой VPS, становятся весомыми, если верно что-то из этого:
- Хочется свой домен. ngrok разрешает свой кастомный домен только на тарифе Pay-as-you-go и выше, не на Free.[1]
- Лимиты бесплатного тарифа мешают. Free-план ngrok стоит $0, но с разовым кредитом $5, и в него входят до 3 онлайн-эндпоинтов, 1 ГБ трафика, 20 000 HTTP/S-запросов в месяц, интерстициальная (предупреждающая) страница на HTTP/S-эндпоинтах и один участник команды.[1] Для демо хватает; для ровного трафика или браузерного эндпоинта, который рекламирует вендор, начинает давить.
- Нужен контроль над приватностью и data-residency. В подходах с SSH reverse-tunnel и собственным релеем открытый текст ходит только между вашей машиной и VPS - без стороннего релея. С ngrok или Cloudflare посередине продакшн-трафик пересекает их инфраструктуру.
- Туннель - постоянный сервис, а не демо. Постоянный линк с home-сервера или однопользовательского приложения - это просто ещё одна часть стека; на своей машине вы контролируете и зону поражения, и биллинг.
Обратный аргумент: у managed-туннелей отличный аптайм, а Pay-as-you-go у ngrok (помесячный биллинг с $20 включённого использования, дальше за использование) дёшев на старте.[1] Если URL нужен изредка - поднимайте ngrok или Quick Tunnel от cloudflared и не берите VPS ради этого. Self-hosting туннеля - это ход «у тебя уже есть VPS под другие задачи», а не повод арендовать новый.
Три способа релеить через свой VPS
Есть три семейства, и отличаются они тем, сколько нового софта вы ставите и кому принадлежит публичная точка входа.
(а) SSH reverse-tunnel + nginx reverse-proxy
Здесь используется только OpenSSH, который уже стоит на обеих машинах. Локальная машина открывает порт remote-forward на VPS; nginx на VPS завершает публичный HTTPS и проксирует на loopback-порт.
Всю схему определяет один факт: по умолчанию OpenSSH биндит remote port-forward только на loopback. Со значением GatewayPorts no по умолчанию форвард -R доступен только с самого VPS, а не из интернета.[3] Собственно, это нам и нужно: nginx (публичный :443) проксирует на 127.0.0.1, и сырой туннельный порт наружу не торчит. Если же захочется открыть форварженный порт на всех интерфейсах - ставьте GatewayPorts clientspecified в sshd_config (или yes) и указывайте адрес в аргументе -R.[3]
На VPS, /etc/ssh/sshd_config:
# держим на loopback, спереди nginx
GatewayPorts no
AllowTcpForwarding yes
С локальной машины держим туннель живым с помощью autossh - обёртки, которая запускает ваш ssh, следит за ним и перезапускает, когда он умирает или перестаёт гонять трафик.[2] Она умеет самоконтролироваться через вспомогательный порт (проверка примерно раз в 10 минут), а можно чище: выключить это через -M 0 и позволить собственным keepalive от OpenSSH ловить мёртвое соединение - этот путь маны autossh и рекомендует для современных версий OpenSSH.[2]
# локальная машина, переживает ребут (через systemd)
autossh -M 0 -N \
-o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" \
-o "ExitOnForwardFailure yes" \
-R 8081:localhost:3000 deploy@vps.example.com
-R 8081:localhost:3000 перенаправляет loopback-порт 8081 VPS на ваш локальный :3000. Из-за ExitOnForwardFailure yes ssh выходит, если дальний порт не смог забиндиться, а autossh перезапустит его, пока не получится, - пара работает как единый механизм, переживающий перезагрузку любого из концов, пока вы этим не занимаетесь.
Дальше nginx на VPS отдаёт ваш домен и проксирует в туннель. TLS здесь настраивается плагином Certbot для nginx, который является installer’ом: он правит ваш server-block, чтобы отдавать HTTPS после выпуска сертификата (итоговый блок стоит перепроверить - тот же плагин не прикрутит за вас дополнительные директивы вроде HTTP/2 или security-заголовков).[4]
# /etc/nginx/sites-available/proxy.example.com
server {
listen 80;
server_name proxy.example.com;
return 301 https://proxy.example.com$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name proxy.example.com;
ssl_certificate /etc/letsencrypt/live/proxy.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proxy.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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 apt install certbot python3-certbot-nginx
sudo certbot --nginx -d proxy.example.com
Когда выбирать это: нужно минимум движущихся частей - никакого демона туннелирования, за которым следить на обеих сторонах. Цена - SSH здесь только туннель: нет виртуальных хостов уровня приложения (второму сервису нужен второй порт -R и location или server в nginx), а весь трафик едет в одном SSH-соединении, так что гигантская передача файлов делит канал с интерактивными сессиями. Это вариант «просто ssh и nginx» - и именно его учит делать сообщество на Hacker News.[12]
(б) Self-hosted туннельный агент (frp, rathole, bore, chisel)
Это полноценные reverse-proxy-демоны: серверная часть (на VPS) и клиентская (на локальной машине). Публичную сторону сервера всё равно прикрываете nginx + Certbot ради TLS; инструмент отвечает за мультиплексирование и NAT-обход между двумя половинами.
frp - самый функциональный рабочая лошадка - «быстрый reverse proxy, помогающий вывести локальный сервер за NAT или файрволом в интернет», лицензия Apache-2.0, актуальный релиз v0.72.0 (04.11.2026).[5] Поддерживает TCP и UDP, HTTP/HTTPS-виртуальные хосты с поддоменами и веб-дашборд. Конфиг (TOML):
# frps.toml (VPS)
bindPort = 7000
# frpc.toml (локальная машина)
serverAddr = "vps.example.com"
serverPort = 7000
[[tcp]]
name = "web"
localIP = "127.0.0.1"
localPort = 3000
remotePort = 8081
rathole - более лёгкий собрат на Rust - «лёгкий и производительный reverse proxy для NAT-обхода, написанный на Rust, альтернатива frp и ngrok», Apache-2.0.[6] Его страница бенчмарка заявляет схожую с frp латентность, но больше соединений и меньше памяти, - правда, честно оговаривается, что замер делался на loopback и отражает CPU-bound-сценарии, а не ваш реальный канал.[6] Если frp кажется тяжёлым, а нужен просто туннель, - обычный поворот в сторону rathole.
bore - минималистичный - «современный простой TCP-туннель на Rust», который делает ровно одно: гонит TCP на удалённый сервер. Без маршрутизации уровня приложения, без своего TLS, лицензия MIT, примерно 400 строк.[7] Клиент ходит на управляющий порт сервера (по умолчанию 7835), запрашивает удалённый порт, сервер слушает его и возвращает соединения; --secret позволяет серверу отсекать клиентов, не ответивших на HMAC-челлендж, правда сам трафик инструмент не шифрует (для этого его и ставят за nginx/certbot).[7] Правильный выбор, когда весь запрос - «публичный TCP-порт на VPS → локальный сервис».
# VPS: управляющий порт 7835, доступ по токену
bore server --secret "$(openssl rand -hex 16)"
# локальная машина: пробрасываем свой :3000 на VPS
bore local 3000 --to vps.example.com --secret "$BORE_SECRET"
chisel - однобинарный TCP/UDP-туннель поверх HTTP(S), лицензия MIT, «быстрый TCP/UDP-туннель через HTTP», актуальная линейка 1.x.[8] Один бинарник работает и сервером, и клиентом, авторизация - --auth user:pass, reverse-форварды выглядят так:
# VPS: chisel server на управляющем порту (при желании прикрываете nginx/certbot ради TLS)
chisel server --port 9122 --auth tunnel:changeme
# локальная машина: сервер слушает свой 8081 и гонит на ваш :3000
chisel client vps.example.com:9122 --auth tunnel:changeme R:8081:localhost:3000
Во всех четырёх случаях TLS завершается в nginx одинаково: направляете server-блок на proxy_pass http://127.0.0.1:8081; и запускаете certbot --nginx ровно как в разделе (а).[4] Выбор по потребности: frp - если нужны виртуальные хосты, поддомены и дашборд; rathole - если хочется фичи frp в бинарнике поменьше; chisel - если нужен один бинарник на TCP+UDP поверх HTTP(S) и авторизация на клиента; bore - если «форвардни мне этот порт» и есть весь спек.
(в) Вендорские туннели на своём домене - Cloudflare Tunnel и Tailscale Funnel
Они «self-hosted» в том смысле, что клиентский демон крутится у вас, но релей - а для Cloudflare и неймсерверы - всё ещё принадлежат вендору. Они в сравнении потому, что это путь к стабильному URL вообще без настройки HTTP.
Cloudflare Tunnel (cloudflared) - демон на вашей машине открывает outbound-only соединения в глобальную сеть Cloudflare, так что нужны ни публичный IP, ни открытые входящие порты.[10] Quick Tunnel - демонстрация нулевого трения: cloudflared tunnel --url http://localhost:8080 печатает временный URL на trycloudflare.com, не требуя аккаунта или домена, но без гарантии аптайма, с лимитом примерно в 200 одновременных in-flight запросов (сверх - HTTP 429), без поддержки Server-Sent Events и с новым hostname при каждом запуске.[9] Для стабильного hostname нужен именованный туннель, а он требует перевода неймсерверов домена на Cloudflare (full setup) - вы отдаёте Cloudflare свой DNS, чтобы получить Cloudflare-managed TLS на своём имени.[10][9]
Tailscale Funnel - tailscale funnel 3000 публикует локальный порт в интернет по HTTPS, пока машина в вашем tailnet (то есть вы уже пользователь Tailscale).[11] Tailscale автоматически выпускает TLS-сертификат для DNS-имени вашего tailnet (что-то вроде your-computer.tailnet-name.ts.net), завершение TLS делает демон Tailscale на узле, а шифрованный туннель идёт через relay-серверы Tailscale, которые - по заявлению Tailscale - расшифровать трафик не могут.[11] Это простейший способ «пустить весь интернет, но в шифрованном виде» из всех здесь рассмотренных, ценой аккаунта Tailscale и маршрутизации каждого байта через инфраструктуру Tailscale.
HTTPS: кто выпускает сертификат
Главная причина поставить nginx перед сырым туннельным портом - TLS, и каждая архитектура решает его по-своему:
- SSH
/агентные реле: вы сами себе хозяин и это плюс -certbot --nginxна VPS для вашего домена, дальше nginx завершает TLS и проксирует на loopback-порт туннеля.[4] Полный контроль над сертификатом и hostname; цена - пара строк nginx выше. - Quick Tunnel: Cloudflare выпускает сертификат для hostname
trycloudflare.comавтоматически; вы ничего не делаете.[9] - Именованный Cloudflare Tunnel: Cloudflare управляет TLS для вашего hostname, но только потому, что зона лежит на неймсерверах Cloudflare.[10]
- Tailscale Funnel: Tailscale выпускает сертификат для имени
*.ts.net; вы ничего не делаете кромеtailscale funnel 3000.[11]
То есть «self-hosted»-варианты покупают вам домен и сертификат, а взамен просят один запуск certbot. certbot --nginx правит конфиг сервера в процессе установки - это ожидаемо, просто прочитайте, что он поменял.[4]
Сравнение бок о бок
| Вариант | Новый софт на вашей машине | Публичная точка входа | TLS | Для чего лучше |
|---|---|---|---|---|
| SSH + autossh + nginx | нет (OpenSSH + nginx) | nginx на VPS | certbot –nginx | Минимум деталей; один основной сервис |
| frp | демоны server + client | nginx на VPS | certbot –nginx | Виртуальные хосты, TCP+UDP, дашборд |
| rathole | демоны server + client | nginx на VPS | certbot –nginx | Фичи frp в бинарнике поменьше |
| bore | демоны server + client | VPS (сырой TCP-порт) | добавляете сами | «Форвардни этот порт», и всё |
| chisel | один бинарник (обе роли) | nginx на VPS | certbot –nginx или в пути | Один бинарник, TCP+UDP поверх HTTP(S) |
| Cloudflare Quick Tunnel | cloudflared | edge Cloudflare | автоматический (CF) | Одноразовая демо-ссылка, ноль настройки |
| Cloudflare named Tunnel | cloudflared | edge Cloudflare | автоматический (CF) | Стабильный URL, DNS уже на CF |
| Tailscale Funnel | Tailscale | relay Tailscale | автоматический (ts.net) | Уже на Tailscale, E2E-шифрование |
Про производительность скажу честно: никто здесь не публикует цифру, которую я бы повторил как замеренный факт. Проект rathole публикует собственный loopback-бенчмарк о большем числе соединений и меньшей памяти при схожей латентности с frp - это измерение самого проекта, сделанное на localhost, и сам проект предупреждает не переносить его на ваш канал.[6] А с уверенностью можно сказать следующее: с завершением TLS в nginx и релеем на собственном VPS пропускная способность ограничена полосой вашего VPS и потолком ваших соединений - плоская предсказуемая цена за безлимитные эндпоинты против метрируемого бесплатного тарифа ngrok ($5 кредит, 3 эндпоинта, 1 ГБ, 20 000 запросов), как только трафик перерастёт демо.[1]
Настоящая графа сделки - централизация и единая точка отказа. Self-hosted означает, что ваш VPS теперь критичен: если лёг - упал и туннель. Managed означает критичен край вендора, поэтому аптайм лучше, но трафик пересекает его сеть. Tailscale Funnel посередине: ему нужны координация и relay Tailscale, но расшифровать проходящее он не может.[11]
Гид по выбору
- Разовое демо, тратиться не хочется: Quick Tunnel от cloudflared.[9] Ноль настройки, эфемерный hostname, новый URL на каждый запуск.
- Вы уже на Tailscale и хотите весь интернет внутрь, зашифрованно и просто:
tailscale funnel 3000.[11] - Хотите свой домен
https://app.example.comи уже платите за VPS: SSH + autossh + nginx для одного сервиса, или chisel/frp, если сервисов будет больше одного.[3][8][5] - Нужны виртуальные хосты, поддомены и дашборд на своей машине: frp.[5]
- Просто «выставить этот TCP-порт в интернет» с токеном: bore.[7]
Что бы вы ни выбрали - держите релей за nginx с сертификатом Let’s Encrypt, чтобы мир говорил по TLS с вашим именем, а туннельный порт никогда не появлялся в адресной строке браузера. Это и есть весь апгрейд относительно голого reverse proxy в первую очередь.[4]
Источники
- ngrok - Pricing (Free: кредит $5, 3 эндпоинта, 1 ГБ трафика, 20 000 запросов, интерстициальная страница; Pay-as-you-go). https://ngrok.com/pricing - checked 2026-10-06
- autossh - Ubuntu manpage: «monitor and restart ssh sessions» (проверка монитор-порта ~10 мин;
-M 0cServerAliveInterval/ServerAliveCountMax). https://manpages.ubuntu.com/manpages/focal/man1/autossh.1.html - checked 2026-10-06 - OpenSSH -
sshd_config(5): remote-форварды биндятся на loopback по умолчанию;GatewayPorts. https://man.openbsd.org/sshd_config - checked 2026-10-06 - Certbot - Using Certbot (authenticators vs installers; плагин nginx правит конфиг сервера). https://eff-certbot.readthedocs.io/en/stable/using.html - checked 2026-10-06
- frp - GitHub-репозиторий (Apache-2.0; релизы: v0.72.0, 04.11.2026). https://github.com/fatedier/frp; https://github.com/fatedier/frp/releases/latest - checked 2026-11-07
- rathole - GitHub-репозиторий + бенчмарк (Rust, Apache-2.0; собственный loopback-бенчмарк: схожая латентность с frp, больше соединений/полосы, меньше памяти). https://github.com/rapiz1/rathole - checked 2026-10-06
- bore - GitHub README (TCP-туннель на Rust, MIT; управляющий порт 7835; секретная авторизация; случайный удалённый порт). https://github.com/ekzhang/bore - checked 2026-10-06
- chisel - GitHub README (быстрый TCP/UDP-туннель через HTTP, MIT;
--auth; reverse-форварды). https://github.com/jpillora/chisel - checked 2026-10-06 - Cloudflare - Quick Tunnels (trycloudflare.com; без аккаунта/домена; без гарантии аптайма; лимит 200 in-flight → 429; без SSE; новый hostname на каждый запуск). https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/ - checked 2026-10-06
- Cloudflare - Cloudflare Tunnel (outbound-only соединения в сеть Cloudflare; именованные туннели требуют домен на неймсерверах Cloudflare). https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/ - checked 2026-10-06
- Tailscale - Funnel docs (
tailscale funnel 3000; авто-TLS для DNS tailnet; relay-серверы не могут расшифровать; терминация на демоне узла). https://tailscale.com/kb/1223/tailscale-funnel; https://tailscale.com/kb/1311/tailscale-funnel - checked 2026-10-06 - Hacker News - «Self-hosted HTTP tunnels with SSH and Nginx» (сигнал тренда из сообщества, не источник факта). https://news.ycombinator.com/item?id=49958569 - checked 2026-10-06