← Все статьи

Как разместить несколько проектов на одном сервере по поддоменам

Практическое руководство по DNS, виртуальным хостам Apache, локальным портам и HTTPS. Разбираем действующую схему 40i.net со статическими сайтами и приложениями на Rails.

Поддомен задаёт адрес, но не изолирует приложение. DNS направляет имя на IP; Apache выбирает сайт по имени хоста; виртуальный хост раздаёт файлы или передаёт запрос локальному процессу. На 40i.net корневой домен раздаёт статическую вики из /var/www/40i-wiki, site-builder.40i.net проксирует запросы к Rails на 127.0.0.1:3000, а другие проекты размещены в собственных каталогах или работают как отдельные приложения. Разберём эту схему от DNS до развёртывания.

Как запрос попадает в нужный проект?

Маршрут состоит из четырёх отдельных решений: DNS сопоставляет имя с IP; TLS выбирает сертификат; Apache выбирает виртуальный хост по запрошенному имени; выбранный хост раздаёт файлы или перенаправляет запрос на локальный порт. Эти уровни легко спутать при сбое. DNS не запускает процесс и не выпускает сертификат, а Apache не поддерживает приложение запущенным. Проверяйте каждый уровень отдельно.

Путь запроса через один сервер
Браузер
  │  https://app.example.com
  ▼
DNS A / wildcard ──► SERVER_IP
  │
  ▼
Apache :443 ── TLS-сертификат + виртуальный хост по Host
  ├── DocumentRoot ──► файлы сайта
  └── ProxyPass ─────► 127.0.0.1:3001 (процесс приложения)

Какие DNS-записи нужны поддоменам?

Для каждого имени проекта создайте запись A на публичный IPv4-адрес сервера. Wildcard-запись *.example.com может направлять остальные поддомены на тот же IP; корневому домену всё равно нужна отдельная запись. Более конкретная DNS-запись имеет приоритет. Wildcard упрощает разрешение имён, но не создаёт сайты. Не меняйте без необходимости почтовые записи MX, SPF, DKIM и DMARC, редактируя DNS сайта.

Почему оставляем Apache, а не переходим на nginx?

Это решение о веб-сервере, который уже обслуживает проекты, а не утверждение, что Apache всегда лучше. На сервере 40i.net установлен и запущен Apache 2.4; nginx не установлен. ISPConfig управляет файлами виртуальных хостов Apache и сопоставлением ACME-проверок, а сайты, журналы и продление сертификатов уже используют эту конфигурацию. ISPConfig поддерживает оба веб-сервера, поэтому сама панель не диктует выбор. Для этого сервера сохранение Apache позволяет не переносить сгенерированные виртуальные хосты и TLS-процедуры и не добавлять ещё один прокси-уровень — при текущей нагрузке это почти ничего не даст. nginx подходит для нового сервера или подтверждённой измерениями потребности; переход требует плана переноса сайтов, владельца портов 80/443, выпуска сертификатов и отката.

Как связаны ISPConfig и веб-сервер
Панель ISPConfig
  └── создаёт и управляет виртуальными хостами и ACME-маршрутом
       │
       ▼
Apache 2.4 (активен на 40i.net)
  ├── статические сайты
  └── обратный прокси ──► приложение на 127.0.0.1:PORT

nginx: на этом сервере не установлен

Как Apache выбирает виртуальный хост?

Apache сравнивает HTTP-заголовок Host со значениями ServerName и ServerAlias виртуальных хостов, слушающих нужный порт. Укажите для каждого хоста явный ServerName; добавляйте ServerAlias только для имён того же проекта. Широкий шаблон *.example.com может перехватить поддомен, принадлежащий другому приложению. Если имя не совпало, Apache отвечает первым подходящим виртуальным хостом. Настройте для неизвестных имён намеренный ответ, чтобы случайно не открыть чужой сайт.

Когда Apache может раздавать файлы напрямую?

Для статического сайта задайте отдельный DocumentRoot и DirectoryIndex; процесс приложения не нужен. Так работают экспортированные HTML-страницы. Корневая вики 40i.net раздаётся именно таким способом; отдельные статические страницы есть у Transcriber и Profile Analyzer. Храните опубликованные файлы в собственном каталоге и открывайте Apache чтение, но не секреты развёртывания или Git-репозиторий. Виртуальный хост разделяет маршрутизацию, а права Unix ограничивают доступ процесса к файлам.

Как проксировать веб-приложение?

Запускайте приложение на отдельном локальном порту и передайте публичные HTTP и HTTPS Apache. Приложение должно слушать 127.0.0.1, чтобы клиент не обходил TLS и маршрутизацию по домену. Сохраняйте исходный Host и схему запроса: они нужны для корректных ссылок, перенаправлений и cookies. Здесь Site Builder проксируется к Rails на порту 3000; другие приложения могут использовать собственные локальные порты. Для WebSocket, например Rails Action Cable, нужно отдельное правило перед общим HTTP-прокси.

Виртуальный хост приложения с локальным backend
<VirtualHost *:443>
    ServerName app.example.com
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/app.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/app.example.com/privkey.pem

    ProxyPreserveHost On
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"
    ProxyPass / http://127.0.0.1:3001/
    ProxyPassReverse / http://127.0.0.1:3001/
</VirtualHost>

Как systemd управляет приложениями?

Создайте отдельную службу systemd для каждого веб-процесса и фонового обработчика. Укажите Unix-пользователя, рабочий каталог, файл окружения и политику перезапуска. Секреты держите вне Git и публичного каталога. В развёртывании Rails веб-процесс и фоновые задачи работают как разные службы. После изменения unit-файла выполните systemctl daemon-reload и перезапустите службу. После изменения переменных окружения перезапустите приложение, чтобы оно перечитало файл. До диагностики Apache проверьте состояние службы и журнал.

Как приложение должно обрабатывать домен?

Прокси обычно сохраняет исходный Host, но приложению всё равно нужен список разрешённых доменов и основной URL. В этом Rails-развёртывании APP_HOST задаёт основной хост, APP_BASE_DOMAIN — основу поддоменов арендаторов, а RAILS_ALLOWED_HOSTS разрешает нужные заголовки. Зарезервируйте www и admin, чтобы маршрутизатор арендаторов не принимал их за клиентские сайты. После смены домена проверьте перенаправления, ссылки входа и сброса пароля, cookies, WebSocket и canonical URL.

Даёт ли wildcard DNS wildcard-сертификат HTTPS?

Нет. DNS определяет адрес, но сертификат требует отдельного подтверждения контроля домена. HTTP-01 Let’s Encrypt получает токен по пути /.well-known/acme-challenge/ через порт 80. DNS-01 проверяет TXT-запись и нужен для wildcard-сертификата. Сертификаты для отдельных имён подходят небольшому стабильному списку; DNS-01 wildcard — часто меняющимся поддоменам, если DNS-провайдер поддерживает безопасную автоматизацию. SAN-сертификат с несколькими именами нужно обновлять, сохраняя в нём все действующие хосты.

Оставьте путь HTTP-01 до перенаправления остальных запросов на HTTPS
Alias /.well-known/acme-challenge/ /path/served/by/certbot/

RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
RewriteRule ^ https://app.example.com%{REQUEST_URI} [R=301,L,NE]

Как устроено продление сертификатов на 40i.net?

Для HTTP-01 Let’s Encrypt должен получить токен для каждого имени. Не перенаправляйте ACME-путь в обход webroot Certbot. В конфигурации 40i.net используется webroot ISPConfig; скрипт синхронизации получает активные имена сайтов и арендаторов, расширяет SAN-сертификат через Certbot и перезагружает Apache. Для site-builder.40i.net выпущен отдельный сертификат. Сохраняйте активные имена при расширении SAN, проверьте загрузку файлов Apache и запустите пробное продление после изменения webroot или DNS.

Как добавить поддомен без влияния на соседей?

Зарезервируйте уникальное имя и проверьте его по списку поддоменов арендаторов и проектов. Направьте DNS на сервер или убедитесь, что имя покрывает wildcard. Выберите каталог для статического сайта либо свободный loopback-порт. Создайте точный виртуальный хост Apache, укажите допустимый домен приложения и canonical URL, подготовьте сертификат. Проверьте конфигурацию Apache до перезагрузки, затем backend локально и публичный HTTPS-адрес. Храните команду развёртывания, unit-файл и конфигурацию хоста в Git или репозитории эксплуатации.

Как найти уровень, где возник сбой?

Проверяйте по порядку: dig +short app.example.com показывает DNS; apachectl -S — выбранный хост; apachectl configtest — ошибки синтаксиса; systemctl status и journalctl — состояние приложения; curl с заголовком Host на 127.0.0.1:PORT проверяет backend без публичного DNS; openssl s_client с именем сервера показывает сертификат Apache. Ошибка 502 обычно связана с процессом или портом upstream; чужая страница — с выбором другого хоста; сертификат не на тот домен — с TLS или конфигурацией виртуального хоста.

Где предел одного сервера?

Поддомены разделяют маршрутизацию, но не процессор, память, диск, базу данных и последствия отказа машины. Перегруженное приложение замедлит соседние; сбой единственного сервера отключит все проекты. Общая база также может стать узким местом. Следите за нагрузкой и проверяйте восстановление резервных копий. Переходите на отдельный сервер или балансируемую схему, когда нужны изоляция, доступность или мощность; до переключения подготовьте на новом адресе DNS и сертификаты.

Проекты по теме

Первоисточники технической документации