При разработке веб-проектов неизбежно поднимается вопрос их размещения в интернете. Разворачивать отдельную виртуальную машину для каждого маленького сервиса иногда избыточно и экономически нецелесообразно. Давайте пристальнее взглянем на проблему размещения множества веб-сервисов на одном виртуальном сервере и познакомимся с элегантным решением — Nginx Proxy Manager. Читать далее →

При разработке веб-проектов неизбежно поднимается вопрос их размещения в интернете. Разворачивать отдельную виртуальную машину для каждого маленького сервиса иногда избыточно и экономически нецелесообразно.
Давайте пристальнее взглянем на проблему размещения множества веб-сервисов на одном виртуальном сервере и познакомимся с элегантным решением — Nginx Proxy Manager.
Проблемы развертывания множества проектовСодержание
→ Проблемы развертывания множества проектов
→ Что такое обратный прокси
→ Подготовка инфраструктуры
→ Установка Nginx Proxy Manager
→ Настройка Nginx Proxy Manager
→ Заключение
Развертывание нескольких независимых веб-сервисов на одном сервере может привести к проблемам: от незначительных неудобств до существенных уязвимостей. Оценим наиболее вероятные риски.
Размещение на привилегированных портахПротокол HTTP использует порт 80, а HTTPS — 443. Это значит, что для доступа по адресу https://example.com сервис должен слушать подключения на порту 443. Проблема в том, что порты от 1 до 1023 считаются привилегированными, то есть прослушивать их могут только приложения с правами суперпользователя.
Работа прототипа с ничем не ограниченными правами — это явная уязвимость, так как ПО, находящееся в разработке, может некорректно обрабатывать входящие запросы и дать злоумышленнику максимальный уровень доступа ко всей операционной системе.
Необходимость запуска приложений от имени root в какой‑то степени может быть устранена с помощью Linux capabilities(7), в частности, флагом CAP_NET_BIND_SERVICE, который позволяет слушать привилегированные порты без максимальных прав. Также можно сделать перенаправление через iptables или предусмотреть в приложении «отказ» от прав суперпользователя после начала прослушивания порта.
Однако это лишь полумеры, потому что в операционной системе есть еще одно ограничение.
Один порт — один слушательВ операционных системах есть фундаментальное ограничение: один сетевой порт в каждый момент времени может слушать только одно программа. Если запустить dev-версию фронтенда приложения на порту 3000 (npm run dev), то бэкэнд не сможет слушать подключения на том же порту: он получит ошибку Address already in use.
Решение простое: вынести разные приложения на разные порты. Например, фронтенд оставить на example.com:3000, а бэкэнд перевести на example.com:3001. Однако такое разделение создает целый ряд задач, которые придется решить.
В браузерах есть механизм безопасности CORS (Cross-Origin Resource Sharing), который запрещает обращения к другим доменам. То есть, если dev-сборка фронтенда на http://example.com:3000 будет обращаться к http://example.com:3001/api, то браузер расценит ее как другой источник и потребует от бэкэнда подтвердить, что с example.com:3000 разрешено делать запросы.
Если проектов больше одного, то необходимо следить, какие порты за каким приложением закреплены и актуализировать настройки брандмауэра. Более того, явное указание порта выглядит странно и может отпугивать обычных пользователей.
Кажется, что домены могут решить проблему, но это не так.
Сервер и доменыДомены — это человекочитаемые «псевдонимы» для адресов в интернете. Человеку проще запомнить example.com, чем 198.51.100.67. Кажется, что можно завести два домена, example.com и test.example.com, сопоставить их с адресом 198.51.100.67 и надеяться, что сервер как-нибудь разберется.
На самом деле сервер не знает о доменах. Браузер сперва обращается к DNS-серверам с вопросом: «Куда ведет example.com?» — и получает ответ: «На 198.51.100.67». После этого браузер делает запрос напрямую к IP-адресу 198.51.100.67. Домен, к которому был сделан запрос, будет виден только приложению, которое возьмется его обрабатывать. Важно: такое приложение может быть только одно.
Не стоит также забывать, что HTTP — это текстовый протокол без шифрования. Любые данные, отправленные с его помощью через интернет, будут видны всем промежуточным узлам в сети. Более того, любой из этих узлов может подменить данные.
Такая незащищенность передачи решается использованием безопасной версией протокола — HTTPS. Большинство современных веб-фреймворков его поддерживают. Однако создание и обновление сертификатов для HTTPS требует доказательства владения веб-сервером, что еще раз усложняет администрирование.
К счастью, для всех проблем есть решение — обратный прокси.
Что такое обратный проксиОбратный прокси (reverse proxy) — это сервер-посредник, который:
принимает входящие запросы от пользователей из интернета;
перенаправляет их на внутренние серверы или приложения;
занимает «популярные» порты 80 и 443;
обрабатывает все входящие соединения и маршрутизирует их согласно правилам в файле конфигурации.
Например, на сервере запущена dev-сборка фронтенда по адресу 127.0.0.1:3000, а бэкэнд работает по адресу 127.0.0.1:8000. Обратный прокси слушает подключения по адресу 198.51.100.67. Тогда для него можно написать такие правила:
для домена example.com запросы по пути, который начинается с /api перенаправлять на 127.0.0.1:8000;
остальные запросы для домена example.com перенаправлять на 127.0.0.1:3000.
Так как обратный прокси-сервер разбирает, принимает и обрабатывает запросы, то возможности маршрутизации практически безграничны. Он может:
определять разные правила для разных доменов, адресов и путей на сервере;
определять права доступа в зависимости от адреса отправителя;
обогащать заголовки запроса дополнительной информацией.
распределять нагрузку между несколькими экземплярами одного приложения.
Такое богатство функций решает практически все проблемы, описанные ранее:
обратный прокси-сервер — это известное и проверенное ПО, готовое для работы в продакшене, и в отличие от прототипа, его запуск с правами суперпользователя несет меньшую угрозу;
у популярных реализаций обратного прокси-сервера есть опция, которая запускает обработчики от имени выделенного «бесправного» пользователя;
порты 80 и 443 заняты специализированным ПО, которое решает вопросы маршрутизации;
если обратный прокси-сервер и конечное приложение находятся в одном доверенном сегменте сети или вовсе на одном сервере, то между ними можно использовать открытый HTTP, что упрощает разработку.
Кроме того, появляется возможность в настройках фаервола открыть только минимально необходимые порты — например, 22, 80 и 443. Внутренние адреса веб-сервисов, конечно нужно отслеживать и прописывать в файле конфигурации обратного прокси, но зато по умолчанию все новые веб-сервисы защищены от «дикого интернета».
Какие обратные прокси-серверы существуютНа момент написания статьи есть несколько различных решений, выполняющих задачи обратного прокси-сервера: nginx, Apache HTTP Server, HAproxy и другие. Стоит отметить, что часть из них способна работать в качестве самостоятельного веб-сервера, то есть раздавать статические файлы из каталога, генерировать листинги директорий и даже запускать CGI- и FastCGI-приложения.
Мы рассмотрим только nginx и графический интерфейс для удобной конфигурации — Nginx Proxy Manager (NPM). Не путайте с Node Package Manager, который имеет такую же аббревиатуру и одноименную команду в терминале — npm.

Снижайте расходы до 80% в облачных серверах Selectel
Подключите прерываемые ВМ и функцию «Заморозка» в панели управления.

Страница заказа сервера.
Сперва подготовим сервер. Заходим в панель управления, далее Продукты → VDS Серверы → Создать сервер. Выбираем подходящую локацию, подходящую по бюджету и ресурсам виртуальную машину и предпочтительную операционную систему.
Мы возьмем 2 vCPU, 2 GB RAM, 40 GB NVMe и Ubuntu 24.04 в Санкт-Петербурге. Хотя это не является обязательным, рекомендуем использовать SSH-ключ для подключения к виртуальной машине вместо пароля. Когда вся информация заполнена, нажимаем Создать сервер. После завершения процесс в карточке сервера появится инструкция по подключению. Проверяем подключение:
ssh root@136.234.X.XВ терминале увидим подробности устанавливаемого соединения. Обратите внимание, что в первый раз необходимо подтвердить свое намерение.
The authenticity of host '136.234.X.X (136.234.X.X)' can't be established.
ED25519 key fingerprint is SHA256:iCwdllXerMRYQa2Vr8NhpmgHXlBnQ8G3wlBAyJ5qQEM.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '136.234.X.X' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-138-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.Приглашение в терминале изменилось — мы на сервере:
root@chell:~#Отлично, сервер готов.
Назначение домена
Окно добавления А-записи.
В панели управления есть раздел по работе с доменами и DNS-зонами. Можно купить новый домен и тут же привязать его к серверу или делегировать имеющийся на NS Selectel. Создаем запись типа A с адресом сервера для поддомена til.example.com. В поле комментарий можно оставить заметку например, «Сервер: Chell».

Созданная запись.
После создания проверяем доступность.
ping til.example.comПримерно каждую секунду должна появляться очередная запись с метриками прохождения ping-пакета:
PING til.f1remoon.ru (136.234.X.X) 56(84) bytes of data.
64 bytes from 136.234.X.X: icmp_seq=1 ttl=57 time=3.19 ms
64 bytes from 136.234.X.X: icmp_seq=2 ttl=57 time=4.83 ms
64 bytes from 136.234.X.X: icmp_seq=3 ttl=57 time=3.11 ms
64 bytes from 136.234.X.X: icmp_seq=4 ttl=57 time=4.68 msПрервать процесс можно по нажатию Ctrl+C:
^C
--- til.f1remoon.ru ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 3.108/3.950/4.830/0.805 msСоздание TLS-сертификата
Раздел управления доменными зонами с кнопкой создания бесплатного TLS-сертификата от Let's Encrypt.
В списке доменных зон можно создать сертификат для домена, который используется для организации подключения по HTTPS. Выбираем Создать под «TLS-сертификатом».

Диалоговое окно выбора сертификата.
Выпускаем сертификат.

Созданный сертификат.
Скачиваем файлы сертификата. Они потребуются нам позже.
Установка Nginx Proxy ManagerNPM создан упрощать настройку nginx, и для максимального комфорта нужен Docker. Сперва установим его, а после — перейдем к развертыванию NPM.
Установка DockerДля Ubuntu есть официальный репозиторий Docker. Добавляем его в источники.
# Добавляем ключ репозитория
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Добавляем сам репозиторий
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
# Обновляем информацию о пакетах
sudo apt updateЗатем устанавливаем Docker:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginПосле установки проверяем, что тот работает.
root@chell:~# sudo docker run hello-worldВ ответ должны увидеть краткую сводку:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4f55086f7dd0: Pull complete
d5e71e642bf5: Download complete
Digest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe895f9cc8f8
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.Развертывание NPMОбратный прокси будет жить в своем отдельном контейнере, но должен иметь доступ к другим определенным контейнерам. Для этого нужно выделить отдельную сеть в среде Docker.
docker network create proxyТеперь запускаем образ NPM.
docker run -d \
--name app \
--network proxy \
--restart unless-stopped \
-e TZ="Europe/Moscow" \
-p 80:80 \
-p 127.0.0.1:81:81 \
-p 443:443 \
-v "/opt/nginx/data:/data" \
-v "/opt/nginx/letsencrypt:/etc/letsencrypt" \
jc21/nginx-proxy-manager:latest
Приветственное сообщение Nginx Proxy Manager.
Открываем til.example.com в браузере и наблюдаем поздравление с запущенным NPM. Пришло время его настраивать!
Обратите внимание, что в команде docker порт 81 не пробрасывается «наружу». Это сделано из соображений безопасности: на этом порту открывается веб-интерфейс панели администратора, который при первом запуске предлагает создать нового пользователя.
Во-первых, такие страницы не стоит делать доступными из интернета, во-вторых, взаимодействие с этим интерфейсом осуществляется по небезопасному протоколу HTTP. Для работе в «админке» лучше воспользоваться SSH-туннелированием:
ssh -N -L 1081:localhost:81 root@til.example.comПосле выполнения этой команды админ-панель будет доступна в браузере по адресу http://localhost:1081.

Рождение нового администратора.
Создаем нового пользователя и попадаем в панель. Теперь мы можем настраивать все, что нужно.
Подключение HTTPS
Окно создания сертификата.
Ранее мы уже выписывали TLS-сертификат. Теперь его нужно передать в Nginx Proxy Manager. Выбираем Certificate → Custom Certificate.

Выбор скачанных файлов.
Указываем ключ и сертификат, как указано на изображении. Промежуточный сертификат (Intermediate Certificate) не указываем, так как у нас его нет. Нажимаем Save. Теперь мы можем использовать доступный сертификат.
Проксирование до другого контейнераПредставим, что один из сервисов разворачивается в собственном контейнере. Запускаем контейнер:
docker run -d \
--name whoami \
--network proxy \
traefik/whoamiОбязательно указываем ключ --network proxy, чтобы NPM имел сетевой доступ к контейнеру. Также запоминаем название контейнера: оно используется в качестве доменного имени.

Создание прокси-хоста.
Переходим на вкладку Hosts → Proxy Hosts и нажимаем Add Proxy Host. Вписываем домен: til.example.com. В Forward Hostname / IP указываем имя контейнера, который хотим сделать доступным, в нашем случае — «whoami». Docker-сеть можно считать доверенной, внутри нее общение ведется по HTTP и, следовательно, порт указываем 80.

Добавление поддержки HTTPS.
Затем переходим на вкладку SSL и указываем ранее загруженный сертификат. Затем нажимаем Save. Если все правильно, то переходим по адресу http://til.example.com.
Происходят две важные вещи. Во-первых, подключение переходит с HTTP на HTTPS. Во-вторых, открывается страница подобного содержания:
Hostname: 16d1ddf20675
IP: 127.0.0.1
IP: ::1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:38082
GET / HTTP/1.1
Host: til.example.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9,ru;q=0.8
Cache-Control: max-age=0
Connection: close
Sec-Ch-Ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Linux"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 198.51.100.69
X-Forwarded-Proto: https
X-Forwarded-Scheme: https
X-Real-Ip: 198.51.100.69Эту страницу отдает контейнер whoami. Если его остановить, то обратный прокси будет возвращать ошибку 502. Отлично, проксирование до других контейнеров работает.
Проксирование до bare-metalМожет возникнуть ситуация, когда приложение по каким-то причинам не может быть помещено в Docker‑контейнер и запускается непосредственно в операционной системе хоста. В этом случае нет имени контейнера и «магии Docker» не случится.
Чтобы проксировать запросы в хостовую систему, необходимо узнать адрес интерфейса, который используется в Docker-сети:
docker network inspect proxy -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}'Вывод этой команды — IP-адрес, например, 172.18.0.1. Именно его нужно использовать в поле Forward Hostname / IP.
ЗаключениеВажно, чтобы приложение слушало подключение именно на этом адресе.
Для Nginx Proxy Manager, работающего в контейнере, адрес 127.0.0.1 — это сам контейнер, а не хостовая ОС. Поэтому NPM просто не увидит приложение.
Если же приложение ожидает подключения на 0.0.0.0, то нужно проверить настройки фаервола: без него приложение будет доступно по публичному адресу и номеру порта в обход обратного прокси-сервера.
Nginx Proxy Manager — прекрасный инструмент, использующий проверенный nginx, но абстрагирующий от установки и редактирования текстовых файлов конфигурации. Благодаря веб-интерфейсу NPM и Docker можно быстро настроить проксирование с разных доменов на нужные контейнеры, то есть максимально эффективно использовать доступные вычислительные ресурсы.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Выделенный сервер: как выбрать конфигурацию и не переплатить | 0 | 8.23 | 30-09-2026 |
| 2 | Два TLS-сервиса на одном 443-м порту: разбор по SNI | 0 | 10.1 | 15-09-2026 |
| 3 | Когда VPS уже недостаточно: зачем нужен выделенный сервер | 0 | 8.83 | 12-10-2026 |
| 4 | Как я переносил работающую Windows-ВМ с Hyper-V на Proxmox, не выключая исходную машину | 0 | 9.98 | 22-09-2026 |
| 5 | Как я писал сервер и нечаянно пробил 1М RPS | 0 | 10 | 31-08-2026 |
| 6 | Каскадный сбой Yandex Cloud: анатомия событий октября 2026 и выживание бизнеса вне одного облака | 0 | 13.44 | 12-10-2026 |
| 7 | RPS одинаковый, но p95 по TTFT хуже в 2,7 раза: что round-robin делает с KV-cache | 0 | 15.44 | 07-10-2026 |
| 8 | Где HTTP/2 и HTTP/3 реально быстрее, а где не дают ничего | 0 | 15.15 | 14-09-2026 |
| 9 | Алгоритм терморегулирования в серверных процессорах Intel | 0 | 7.53 | 08-10-2026 |
| 10 | Миграция без права на ошибку: как перенести 70 кластеров MongoDB в 7 и не сломать продакшен | 0 | 10.65 | 02-09-2026 |