Несколько сайтов на одном VPS: Nginx и server_name
Один VPS вполне выдерживает несколько небольших проектов одновременно — если правильно развести их в конфигурации Nginx.
Когда одного сайта недостаточно
Ресурсов среднего VPS обычно хватает на 3-5 небольших проектов: блог, лендинг, панель мониторинга, тестовый стенд для клиента. Держать под каждый отдельный сервер бессмысленно — большую часть времени такие сайты простаивают, а не грузят процессор. Проблема не в мощности, а в организации: если конфиги Nginx свалены в один файл или названы как попало, рано или поздно один сайт случайно перекроет другой либо конфиг перестанет открываться вовсе.
Дальше предполагается, что базовая настройка Nginx и HTTPS для одного домена уже сделана — если ещё нет, сначала пройдите гайд по Nginx и HTTPS. Здесь мы просто размножаем ту же схему на несколько доменов и следим, чтобы они не мешали друг другу.
Структура конфигов
Правило простое: один сайт — один файл. Он живёт в /etc/nginx/sites-available/, а в /etc/nginx/sites-enabled/ кладётся только симлинк на него. Активными считаются лишь те конфиги, у которых есть симлинк — это удобно, чтобы временно «выключить» сайт, не удаляя его настройки.
sudo nano /etc/nginx/sites-available/site2.example.com
sudo ln -s /etc/nginx/sites-available/site1.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/site2.example.com /etc/nginx/sites-enabled/
Называйте файлы по домену, а не «site1», «site2» — через полгода вы забудете, какой файл за что отвечает. Симлинк без флага -s создаст копию файла вместо ссылки, и правки в sites-available перестанут применяться — это частая причина «я поправил конфиг, а ничего не изменилось».
server_name и виртуальные хосты
Все сайты на сервере слушают одни и те же порты 80 и 443. Nginx понимает, какой конфиг отдавать конкретному запросу, по заголовку Host, который присылает браузер — за это отвечает директива server_name.
listen 80;
server_name site1.example.com;
root /var/www/site1;
index index.html;
}
server {
listen 80;
server_name site2.example.com;
proxy_pass http://127.0.0.1:3001;
}
Каждый блок server обслуживает свой домен: один отдаёт статические файлы, другой проксирует запрос на приложение. Если запрос пришёл с Host, которого нет ни в одном server_name, Nginx отдаёт его первому попавшемуся серверу в порядке чтения конфигов — обычно это не то, что вы хотите. Явно пометьте один сайт как сервер по умолчанию, чтобы поведение было предсказуемым.
listen 80 default_server;
server_name _;
return 444;
}
Такой блок с пустым server_name _ и return 444 тихо обрывает соединение для запросов без известного домена — например от сканеров, которые ходят по IP-адресу напрямую.
Поддомены vs отдельные домены
Технически Nginx не видит разницы между app1.example.com, app2.example.com и совершенно другим доменом — в обоих случаях это просто значение server_name. Разница — в организации проектов и в DNS.
Поддомены одного домена удобны, когда сайты логически связаны: основной сайт, панель администратора, API. Для них достаточно создать несколько A-записей у одного домена, указывающих на один и тот же IP VPS:
app2.example.com. A 203.0.113.10
api.example.com. A 203.0.113.10
Отдельные домены имеют смысл, когда проекты никак не связаны — например личный сайт и сайт клиента. Тогда у каждого домена своя A-запись, но конечная точка та же самая:
site-two.com. A 203.0.113.10
В обоих случаях в Nginx просто появляется ещё один блок server с соответствующим server_name — разницы в конфигурации нет, разница в том, как вы объясняете структуру самому себе через полгода.
HTTPS для каждого сайта
Let's Encrypt не ограничивает число сертификатов на один сервер — на одном IP спокойно уживаются десятки независимых сертификатов, по одному на домен (или один на группу доменов, если явно перечислить их через несколько -d). certbot запускается отдельно для каждого сайта:
sudo certbot --nginx -d site2.example.com
sudo certbot --nginx -d app1.example.com -d app2.example.com
certbot сам найдёт нужный блок server по server_name, допишет в него listen 443 ssl, пути к сертификату и редирект с 80 на 443. Проверить, какие домены уже покрыты сертификатами, можно так:
Автопродление одно на весь сервер — отдельный systemd-таймер обновляет все выпущенные сертификаты разом, ничего дополнительно настраивать для каждого нового сайта не нужно:
Изоляция проектов
Когда сайтов несколько, важно, чтобы падение одного не утаскивало за собой остальные. Nginx перед ними в любом случае устоит — сам веб-сервер не падает от ошибки одного проксируемого приложения, — но стоит подстраховаться и на уровне самих приложений.
Простой и надёжный вариант — разносить проекты по портам upstream-приложений. Каждое приложение (свой Docker Compose проект или systemd-сервис) слушает только localhost на своём порту, а Nginx проксирует к нему трафик по домену:
Проект 2: 127.0.0.1:3002
Проект 3: 127.0.0.1:3003
listen 443 ssl;
server_name app1.example.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Приложения при этом не видят друг друга и не могут случайно обратиться не в свою базу данных или занять чужой порт. Дополнительно стоит держать каждый проект в своей папке (например /srv/app1, /srv/app2) и, если сервер обслуживает разных клиентов, заводить под каждый проект отдельного системного пользователя — тогда утечка в одном приложении не даёт доступа к файлам остальных.
Проверка конфигурации
Перед любым применением изменений Nginx проверяет синтаксис конфигов, но не проверяет его автоматически — это нужно делать вручную командой nginx -t:
Если проверка прошла успешно, применяйте изменения через reload, а не restart. Reload перечитывает конфиги и подхватывает новые сайты без разрыва уже открытых соединений на других сайтах — restart на секунду обрывает все сервисы на сервере, включая те, которые вы не трогали.
nginx -t перед reload или restart. Один синтаксически неверный конфиг — например забытая точка с запятой в блоке одного сайта — не даст Nginx запуститься вообще, и это положит сразу все сайты на сервере, а не только тот, где была ошибка.Типовые ошибки
- Два блока с
default_serverна одном порту — Nginx откажется стартовать, пока не останется только один. - Забытый симлинк в
sites-enabled— конфиг лежит вsites-available, но не активен, и правки в нём как будто ничего не меняют. - Сертификат выписан не на тот домен или без нужного поддомена — certbot подставит его не в тот блок
server, и HTTPS у сайта не заработает. - Совпадающий порт upstream-приложения у двух разных проектов — второе приложение либо не запускается, либо трафик неожиданно уходит не туда.
- Общий
server_nameу двух конфигов сразу — Nginx использует только первый найденный блок, а второй сайт становится недостижим.
Что дальше
Несколько открытых в интернет сайтов на одном сервере — это уже несколько точек входа для атак, поэтому часть сервисов разумно вообще спрятать от посторонних глаз: настроим VPN, чтобы часть сервисов оставалась вообще недоступна из открытого интернета →