Nginx: virtual hosts и структура сайтов — как обслуживать несколько проектов правильно
Практическое руководство по настройке virtual hosts (server blocks) в Nginx: несколько сайтов на одном сервере, правильная структура каталогов, разделение логов, default server. Вторая часть серии о настройке веб-сервера.
Требования
- Базовое знание Linux/терминала
- Доступ к серверу (VPS или локальный)
- Прочитана первая часть серии про Nginx
Nginx: virtual hosts и структура сайтов — как обслуживать несколько проектов правильно
Часть 2 серии про Nginx В первой части мы разобрали, как устроен Nginx и где живут конфигурации. Теперь — самое важное для практики: virtual hosts (server blocks), несколько сайтов на одном сервере и правильная структура каталогов.
Введение
Если ты уже знаешь, где лежит nginx.conf и что такое server блок, пора переходить к реальной практике — настройке нескольких сайтов на одном сервере.
В этой статье разберём:
- как Nginx определяет, какой сайт обрабатывать;
- где хранить конфиги для разных сайтов;
- правильную структуру каталогов проектов;
- типовые ошибки и как их избежать;
- практические примеры для Ubuntu/Debian и CentOS/Rocky.
Всё строго по официальной документации Nginx, без фантазий.
Задача, которую решает virtual host
Типичная ситуация:
-
один сервер;
-
несколько доменов:
site1.rusite2.ruapi.site3.ru;
-
каждый сайт — отдельный проект, своя логика, свои логи.
👉 Virtual host (server block) позволяет Nginx понять, какому сайту принадлежит входящий запрос.
Как Nginx выбирает нужный сайт
Алгоритм простой:
- Клиент подключается к IP и порту (
80или443) - В HTTP-заголовке передаётся
Host - Nginx ищет
server_name, который совпадает - Если не нашёл — берёт первый server block как default
Базовый server block (минимум)
Минимальная рабочая конфигурация для одного сайта:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.html index.php;
location / {
try_files $uri $uri/ =404;
}
}
Что здесь происходит
listen 80— слушаем HTTP на порту 80server_name example.com www.example.com— домены, для которых применяется этот блокroot /var/www/example.com/public— корневая директория сайтаindex index.html index.php— файлы, которые Nginx ищет при запросе к директорииtry_files $uri $uri/ =404— проверяет существование файла, затем директории, иначе возвращает 404
Полный пример с логами
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.html index.php;
access_log /var/www/example.com/logs/access.log;
error_log /var/www/example.com/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
Где хранить конфиги сайтов (важно)
Ubuntu / Debian (рекомендуемый подход)
/etc/nginx/
├── sites-available/
│ ├── site1.conf
│ └── site2.conf
└── sites-enabled/
├── site1.conf -> ../sites-available/site1.conf
└── site2.conf -> ../sites-available/site2.conf
Активация сайта:
# Создаём симлинк
ln -s /etc/nginx/sites-available/site1.conf /etc/nginx/sites-enabled/
# Проверяем конфигурацию
nginx -t
# Если проверка прошла успешно, перезагружаем
systemctl reload nginx
Пример полного конфига для Ubuntu/Debian
Создаём файл /etc/nginx/sites-available/mysite.conf:
server {
listen 80;
server_name mysite.ru www.mysite.ru;
root /var/www/mysite.ru/public;
index index.html;
access_log /var/www/mysite.ru/logs/access.log;
error_log /var/www/mysite.ru/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
Активируем:
ln -s /etc/nginx/sites-available/mysite.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
Отключение сайта
# Удаляем симлинк
rm /etc/nginx/sites-enabled/mysite.conf
# Проверяем и перезагружаем
nginx -t && systemctl reload nginx
CentOS / Alma / Rocky
/etc/nginx/conf.d/
├── site1.conf
├── site2.conf
└── api.site3.conf
Тут всё проще — файл есть → сайт активен. Симлинки не нужны.
Пример конфига для CentOS/Rocky
Создаём файл /etc/nginx/conf.d/mysite.conf:
server {
listen 80;
server_name mysite.ru www.mysite.ru;
root /var/www/mysite.ru/public;
index index.html;
access_log /var/www/mysite.ru/logs/access.log;
error_log /var/www/mysite.ru/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
Проверяем и перезагружаем:
nginx -t && systemctl reload nginx
Отключение сайта в CentOS/Rocky
# Переименовываем файл (добавляем .disabled)
mv /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.disabled
# Или удаляем
rm /etc/nginx/conf.d/mysite.conf
nginx -t && systemctl reload nginx
Правильная структура каталогов сайтов
❌ Плохо (часто вижу в проде)
/var/www/
├── site1
├── site2
├── site3
Без логики, всё вперемешку.
✅ Хорошо (чётко и масштабируемо)
/var/www/
├── site1.ru/
│ ├── public/
│ ├── logs/
│ └── releases/ (если есть деплой)
├── site2.ru/
│ ├── public/
│ └── logs/
Почему public — must have
- безопасность (нет доступа к
.env, конфигам, vendor); - единый подход для PHP, Python, Node;
- удобно для CI/CD.
Root vs alias — не путай
Это критически важно понимать разницу, иначе будут 404 ошибки.
root (90% случаев)
Директива root добавляет путь из location к указанному пути:
location / {
root /var/www/site1.ru/public;
}
Как работает:
- URL:
/img/logo.png - Файл на диске:
/var/www/site1.ru/public/img/logo.png - Nginx берёт
root+location+URI
Пример с вложенным location
server {
root /var/www/site1.ru/public;
location / {
try_files $uri $uri/ =404;
}
location /static/ {
# root уже определён выше, путь будет:
# /var/www/site1.ru/public/static/file.css
expires 30d;
}
}
alias (осторожно)
Директива alias заменяет путь из location:
location /media/ {
alias /data/uploads/;
}
Как работает:
- URL:
/media/file.jpg - Файл на диске:
/data/uploads/file.jpg - Nginx берёт
alias+URI(без/media/)
❌ Частая ошибка с alias
# НЕПРАВИЛЬНО
location /media {
alias /data/uploads/;
}
При запросе /media/file.jpg Nginx будет искать /data/uploads//file.jpg (двойной слэш).
✅ Правильно
location /media/ {
alias /data/uploads/;
}
Или:
location /media {
alias /data/uploads;
}
👉 Правило: если в location есть завершающий слэш, он должен быть и в alias.
Когда использовать alias
Используй alias, когда нужно отдать файлы из другой директории, не связанной со структурой сайта:
# Статические файлы из отдельного хранилища
location /uploads/ {
alias /mnt/storage/uploads/;
}
# Или файлы из другого проекта
location /legacy/ {
alias /var/www/old-site/public/;
}
Несколько сайтов на одном IP
Один сервер, один IP, несколько доменов — классическая задача.
Пример конфигурации
# Сайт 1
server {
listen 80;
server_name site1.ru www.site1.ru;
root /var/www/site1.ru/public;
index index.html;
access_log /var/www/site1.ru/logs/access.log;
error_log /var/www/site1.ru/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
# Сайт 2
server {
listen 80;
server_name site2.ru www.site2.ru;
root /var/www/site2.ru/public;
index index.html;
access_log /var/www/site2.ru/logs/access.log;
error_log /var/www/site2.ru/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
# API поддомен
server {
listen 80;
server_name api.site3.ru;
root /var/www/api.site3.ru/public;
index index.php;
access_log /var/www/api.site3.ru/logs/access.log;
error_log /var/www/api.site3.ru/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
Как это работает
- Клиент отправляет запрос на IP сервера
- В HTTP-заголовке
Hostпередаётся домен (например,Host: site1.ru) - Nginx ищет
server_name, который совпадает сHost - Если совпадение найдено — применяется конфигурация этого блока
- Если не найдено — используется default server (или первый блок)
Работает за счёт HTTP-заголовка Host (RFC 7230).
Default server — защита от мусора
Обязательно настраивай default server, иначе Nginx будет использовать первый server блок как дефолтный.
Зачем нужен default_server
Без default server:
- Запросы по IP напрямую попадают в первый
serverблок - Запросы с неизвестными доменами тоже попадают туда
- Лишние логи, лишняя нагрузка
Правильная настройка
server {
listen 80 default_server;
server_name _;
return 444;
}
Что здесь происходит:
listen 80 default_server— этот блок становится дефолтным для порта 80server_name _— специальное значение, означает “любой домен, который не совпал”return 444— закрывает соединение без ответа (экономит трафик)
Альтернативный вариант (с ответом)
server {
listen 80 default_server;
server_name _;
return 403;
}
Возвращает 403 Forbidden вместо закрытия соединения.
Проверка default server
# Запрос по IP должен вернуть 444 или 403
curl -H "Host: unknown-domain.com" http://YOUR_SERVER_IP
👉 Запросы без домена или с неизвестными доменами → сразу отклоняются. Меньше логов, меньше шума, меньше попыток атак.
Логи: разделяй всегда
Никогда не используй общие логи для всех сайтов. Каждый сайт — свои логи.
Базовая настройка
server {
listen 80;
server_name site1.ru;
root /var/www/site1.ru/public;
access_log /var/www/site1.ru/logs/access.log;
error_log /var/www/site1.ru/logs/error.log;
}
Расширенная настройка с уровнями
server {
listen 80;
server_name site1.ru;
root /var/www/site1.ru/public;
# Access log с форматом
access_log /var/www/site1.ru/logs/access.log;
# Error log с уровнем (debug, info, notice, warn, error, crit)
error_log /var/www/site1.ru/logs/error.log warn;
}
Отключение логов для статики
server {
listen 80;
server_name site1.ru;
root /var/www/site1.ru/public;
access_log /var/www/site1.ru/logs/access.log;
error_log /var/www/site1.ru/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
# Статика без логов (экономия места)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 30d;
access_log off;
}
}
Создание директорий для логов
# Создаём директорию для логов
mkdir -p /var/www/site1.ru/logs
# Устанавливаем права (nginx должен писать)
chown -R www-data:www-data /var/www/site1.ru/logs
chmod 755 /var/www/site1.ru/logs
👉 Потом ты скажешь себе спасибо, когда нужно будет найти ошибку конкретного сайта.
Типовые ошибки при работе с virtual hosts
❌ Два сайта с одинаковым server_name
# Конфиг 1
server {
server_name example.com;
}
# Конфиг 2
server {
server_name example.com; # КОНФЛИКТ!
}
Решение: каждый домен — один server_name.
❌ Забыл reload после изменения конфига
# Изменил конфиг, но забыл перезагрузить
vim /etc/nginx/sites-available/site.conf
# Изменения не применятся!
Решение: всегда делай nginx -t && systemctl reload nginx.
❌ Конфиг не подключён через include
# В nginx.conf нет include
http {
# sites-enabled не подключён!
}
Решение: проверь nginx.conf, должен быть:
http {
include /etc/nginx/sites-enabled/*;
}
❌ Неправильный root
server {
root /var/www/site.ru; # Без /public
# Файлы доступны напрямую, включая .env, config.php и т.д.
}
Решение: всегда указывай root на /public директорию.
❌ Нет default_server
# Первый блок становится дефолтным автоматически
server {
server_name site1.ru; # Станет default для всех запросов
}
Решение: явно укажи default_server для мусорного блока.
❌ Неправильные права на файлы
# Файлы недоступны для чтения
chmod 000 /var/www/site.ru/public/index.html
# Nginx вернёт 403
Решение: проверь права:
chown -R www-data:www-data /var/www/site.ru
chmod -R 755 /var/www/site.ru
chmod -R 644 /var/www/site.ru/public/*
❌ Опечатка в server_name
server {
server_name mysite.ru; # Опечатка: должно быть mysite.com
}
Решение: всегда проверяй домен в server_name.
Мини-чеклист перед запуском сайта
Перед тем как считать сайт готовым, проверь:
- Конфиг лежит в нужной директории (
sites-availableдля Ubuntu/Debian,conf.dдля CentOS/Rocky) - Для Ubuntu/Debian: создан симлинк в
sites-enabled -
server_nameсовпадает с реальным доменом -
rootуказывает на/publicдиректорию - Логи раздельные для каждого сайта
- Директории для логов созданы и имеют правильные права
-
nginx -tпроходит без ошибок -
systemctl reload nginxвыполнен успешно - Сайт открывается по домену
- Default server настроен для защиты от мусорных запросов
Команды для проверки
# Проверка конфигурации
nginx -t
# Проверка статуса
systemctl status nginx
# Просмотр активных конфигов (Ubuntu/Debian)
ls -la /etc/nginx/sites-enabled/
# Просмотр всех конфигов (CentOS/Rocky)
ls -la /etc/nginx/conf.d/
# Проверка, какие server блоки активны
nginx -T 2>/dev/null | grep -A 5 "server_name"
Что будет в части 3
Следующий шаг — PHP-FPM и связка с Nginx:
- как Nginx передаёт PHP-запросы;
- сокет vs TCP;
- типовые ошибки 502 / 504;
- структура под PHP-проекты (Bitrix, Laravel, WordPress).
Итог
Virtual hosts (server blocks) — это основа масштабируемости Nginx.
Если сразу выстроить правильную структуру:
- сайты не мешают друг другу;
- конфиги читаются и легко поддерживаются;
- логи разделены, легко найти проблему;
- сервер не превращается в помойку;
- безопасность выше (правильный
root, default server).
Это тот фундамент, который экономит десятки часов в будущем и избавляет от головной боли при масштабировании.
Полезные материалы
Практические сниппеты для работы с virtual hosts:
- Несколько virtual hosts на одном сервере — настройка нескольких сайтов для Ubuntu/Debian и CentOS/Rocky
- Default server для защиты от мусорных запросов — настройка default_server для отклонения запросов по IP
- Раздельные логи для каждого virtual host — настройка отдельных access_log и error_log для каждого сайта
- Правильная структура каталогов для нескольких сайтов — организация директорий public, logs, releases
Официальная документация Nginx:
- Server names — как работает
server_name - How nginx processes a request — алгоритм выбора server block
- Configuring NGINX as a Web Server — официальное руководство



Комментарии