Виртуальные хосты: server и server_name

Рейтинг: 62.3% · 11 голосов
Самый подробный курс по Nginx - от первого конфига до production API-gateway. Архитектура и event loop, контексты и location, виртуальные хосты, статика, reverse proxy и upstream-балансировка, кэширование, HTTPS и TLS (post-quantum), HTTP/2 и HTTP/3 QUIC, безопасность и rate limiting, realip, JSON-логи и сквозная трассировка, метрики, тюнинг и траблшутинг. Плюс OpenResty (Lua, фазы, JWT, динамика) и Angie (нативные Prometheus, ACME, dynamic upstream). На основе эталонного gateway и лучших практик. Для новичков и SRE/DevOps. Актуально на 2026 (nginx 1.30/1.31, Angie, ingress-nginx EOL -> Gateway API).
Ответить
Аватара пользователя
Igor_NgINX
Сообщения: 44
Зарегистрирован: 11 май 2026, 05:31

Виртуальные хосты: server и server_name

Сообщение Igor_NgINX »

Оглавление курса (44)
  1. Что такое Nginx и почему он захватил веб
  2. Установка Nginx, Angie и OpenResty: пакеты, версии, Docker
  3. Первый конфиг с нуля: минимальный сервер за 5 минут
  4. Архитектура процессов: master, worker и event loop
  5. Анатомия конфигурации: контексты, наследование и модульность
  6. Виртуальные хосты: server и server_name (вы здесь)
  7. Директива location: матчинг и приоритеты по шагам
  8. Раздача статики: root, alias, try_files, sendfile
  9. Переменные, map и блок if
  10. rewrite, return и канонизация URL
  11. Работа с заголовками: add_header и proxy_set_header
  12. Модель доверия прокси-заголовков: realip и защита от спуфинга
  13. Сжатие: gzip, brotli и zstd
  14. Reverse proxy: proxy_pass и проброс запроса
  15. upstream и балансировка нагрузки
  16. Таймауты, повторы и устойчивость прокси
  17. FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)
  18. WebSocket, gRPC и стриминг через Nginx
  19. Кэширование ответов: proxy_cache и микрокэш
  20. HTTPS и TLS: сертификаты, протоколы, шифры (2026)
  21. Let's Encrypt и ACME: certbot, acme.sh, нативный ACME Angie
  22. HTTP/2 и HTTP/3 (QUIC) в 2026
  23. Безопасность: заголовки, скрытие версии, ограничения
  24. Rate limiting и защита от перегрузки
  25. Контроль доступа: allow/deny, auth_basic, auth_request и mTLS
  26. Логирование: access_log, форматы и структурированный JSON
  27. Сквозная трассировка запросов и OpenTelemetry
  28. Метрики и мониторинг Nginx
  29. Производительность и тюнинг под нагрузку
  30. Траблшутинг: 502, 504, 403, 404 и debug-лог
  31. OpenResty: Nginx как платформа на LuaJIT
  32. Фазы обработки запроса и Lua-хуки
  33. Lua API: ngx.*, shared dict, cosocket и lua-resty-core
  34. Практика OpenResty: авторизация, JWT, кэш, Redis
  35. Angie: современный форк Nginx 2026
  36. Динамическая конфигурация и service discovery
  37. Nginx как API-gateway и Kubernetes Gateway API
  38. Динамические модули, njs и WAF
  39. Stream-модуль: проксирование TCP и UDP
  40. Деплой, перезагрузка и конфиг как код
  41. Сценарий: статика, SPA и кэширование за CDN
  42. Сценарий: микросервисный gateway по образцу ngx-trace-gateway
  43. Капстоун: собираем production-gateway с нуля
  44. Чек-лист безопасности, CVE 2026 и аудит конфигурации
У тебя один сервер, один Nginx и три домена: shop.example.com, blog.example.com и api.example.com. И все они должны жить на одном IP, на одном 80-м и 443-м порту, но отвечать разным контентом. Как Nginx понимает, какому из трёх сайтов адресован запрос, если TCP-соединение для всех одно и то же? Вот это и есть виртуальные хосты - сердце того, почему один nginx виртуальный хост умеет тащить десятки и сотни сайтов сразу.

Боль, которую мы лечим в этом уроке: ты добавил второй сайт, перезагрузил конфиг, а он почему-то отдаёт контент первого. Или ты убрал www, а половина ссылок отвалилась. Или поставил http2 on старым способом и получил [warn] при каждом nginx -t. Разберём механику до винтика, чтобы ты раз и навсегда понял, как nginx несколько сайтов разруливает по Host и listen.

Что такое server-блок и зачем он нужен

Базовая единица в Nginx, отвечающая за один логический сайт - это nginx server блок. Внутри http { ... } ты можешь объявить сколько угодно блоков server { ... }, и каждый из них - отдельный виртуальный хост. Минимальный пример на два сайта:

Код: Выделить всё

http {
    server {
        listen 80;
        server_name shop.example.com;
        root /var/www/shop;
    }

    server {
        listen 80;
        server_name blog.example.com;
        root /var/www/blog;
    }
}
Оба слушают 80-й порт, оба на одном IP. Когда приходит HTTP-запрос, браузер кладёт в него заголовок Host: shop.example.com или Host: blog.example.com - это и есть ключ, по которому Nginx раскидывает трафик. Без этого заголовка (а в HTTP/1.1 он обязателен) виртуальные хосты по имени были бы невозможны. Имя name-based virtual hosting означает ровно это: различаем по имени из Host, а не по IP или порту.

Изображение

Директива listen: порт, IP, default_server и флаги

Директива nginx listen говорит, на каком сокете слушать. Полная форма умеет куда больше, чем просто номер порта:

Код: Выделить всё

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl default_server reuseport;
    server_name example.com;
}
Разберём по частям. listen 80 - слушать все IPv4-адреса на 80-м порту. listen [::]:80 - то же для IPv6 (квадратные скобки - синтаксис IPv6-адреса). Это два разных сокета, и если хочешь принимать клиентов по IPv6, вторую строку надо писать явно. В эталонном проекте http_server.conf именно так и сделано: listen 80 default_server плюс отдельная строка для IPv6 - не забывай про неё, иначе IPv6-клиенты просто не достучатся.

Флаги после порта:
  • default_server - этот блок становится дефолтным для данной пары адрес:порт. О нём чуть ниже отдельно, это важная штука.
  • ssl - на этом сокете терминируется TLS. Без него ssl_certificate работать не будет.
  • reuseport - включает опцию сокета SO_REUSEPORT: ядро создаёт отдельную слушающую очередь на каждый воркер и само балансирует входящие соединения между ними. Снимает локовую толкотню на accept под высокой нагрузкой. Указывается ОДИН раз на пару адрес:порт, не в каждом server.
Главная ловушка 2026 года - HTTP/2. Старый способ listen 443 ssl http2 устарел ещё с nginx 1.25.1. Если напишешь так на современном nginx (а у нас stable 1.30.0 от 14 апреля 2026 и mainline 1.31.x), получишь предупреждение:

Код: Выделить всё

nginx: [warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead
Правильно - ОТДЕЛЬНОЙ директивой http2 on внутри server-блока:

Код: Выделить всё

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;
    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;
}
Почему так лучше: раньше http2 был свойством сокета, и при нескольких server на одном listen возникала путаница, какой из них определяет протокол. Теперь http2 - свойство конкретного server-блока, что логичнее и убирает целый класс конфликтов. Заодно отмечу: server push из HTTP/2 удалён из nginx (директив http2_push больше нет), современная замена - 103 Early Hints, появившиеся в 1.30. А HTTP/3 на QUIC уже давно production-ready (в stable с 1.30, в официальных пакетах и docker-образах), и включается он своими listen 443 quic reuseport плюс http3 on - это тема отдельного урока, но знай, что http2 и http3 - именно отдельные директивы, а не флаги listen.

server_name: точное имя, wildcard, regex, пустое и подчёркивание

Директива nginx server_name задаёт, на какие значения заголовка Host реагирует блок. Форм несколько, и Nginx обрабатывает их с разным приоритетом:

Код: Выделить всё

server { server_name example.com www.example.com; }   # точные имена
server { server_name *.example.com; }                  # wildcard слева
server { server_name mail.*; }                         # wildcard справа
server { server_name ~^www\d+\.example\.com$; }        # regex (тильда)
server { server_name "";  }                            # пустой Host
server { server_name _;   }                            # "невалидное имя"
Точное имя - перечисляешь домены через пробел, можно несколько. Wildcard *.example.com ловит api.example.com, shop.example.com, но НЕ голый example.com и НЕ a.b.example.com (звёздочка - ровно одна метка). Звёздочка допускается только в самом начале или самом конце имени. Regex начинается с тильды ~, внутри можно делать именованные захваты и использовать их дальше как переменные - удобно для шаблонных поддоменов вроде user123.example.com.

Особые случаи. server_name "" (пустая строка) ловит запросы вообще без заголовка Host - такие шлют кривые боты и старые HTTP/1.0 клиенты. server_name _ - это НЕ wildcard и не "любой хост". Подчёркивание - просто синтаксически невалидное доменное имя, которое гарантированно никогда не совпадёт ни с одним реальным Host. Его используют как заглушку для дефолтного блока-ловушки, чтобы тот не перехватывал чужие имена случайно. Запомни: _ ничего не матчит сам по себе, он ловит трафик только за счёт связки с default_server, а не за счёт имени.

Алгоритм выбора server: как Nginx находит нужный виртуальный хост

Это ядро урока. Когда приходит запрос, Nginx делает строго по шагам:
  • Шаг 1. Берёт IP и порт, на которые пришло соединение, и заголовок Host из запроса.
  • Шаг 2. Отбирает все server-блоки, у которых listen совпадает с этой парой адрес:порт. Точное совпадение адреса (listen 1.2.3.4:80) приоритетнее, чем wildcard-адрес (listen 80, то есть *:80).
  • Шаг 3. Среди отобранных ищет server_name, совпадающий с Host, в таком порядке приоритета: точное имя, затем самый длинный wildcard, начинающийся со звёздочки (*.example.com), затем самый длинный wildcard, заканчивающийся звёздочкой (mail.*), затем первый по порядку regex, который сматчился.
  • Шаг 4. Если по имени ничего не нашлось - запрос уходит в default_server для этой пары адрес:порт.
Ключевой момент про шаг 4: nginx default_server существует ВСЕГДА. Если ты явно не пометил ни один блок флагом default_server, дефолтным становится ПЕРВЫЙ по порядку server-блок для данной пары адрес:порт. Отсюда классическая граната под ногами: ты добавил новый сайт первым в файле, забыл про server_name, и теперь весь "ничейный" трафик (включая сканеры по голому IP) валится именно в него. Поэтому правило хорошего тона - всегда иметь явный default_server-ловушку.

Проверим логику на конкретном наборе:

Код: Выделить всё

server { listen 80 default_server; server_name _; return 444; }
server { listen 80; server_name shop.example.com; root /var/www/shop; }
server { listen 80; server_name *.example.com;    root /var/www/wild; }
server { listen 80; server_name ~^(?<sub>.+)\.example\.com$; root /var/www/re; }
Host: shop.example.com -> точное имя побеждает wildcard и regex, попадаем в /var/www/shop. Host: api.example.com -> точного нет, ловит *.example.com (/var/www/wild), regex даже не проверяется. Host: deep.api.example.com -> wildcard не подходит (две метки слева), а regex .+ ловит всё - идём в /var/www/re, и переменная $sub будет равна deep.api. Host: evil.org или запрос по голому IP -> ничего не совпало, дефолтный блок отдаёт return 444 (Nginx молча рвёт соединение - удобно гасить мусорных ботов).

SNI: как выбирается server для HTTPS

С HTTPS есть тонкость. Заголовок Host лежит ВНУТРИ зашифрованного запроса, а сертификат сервер должен предъявить ДО расшифровки, на этапе TLS-хендшейка. Откуда тогда Nginx знает, чей сертификат показать?

Ответ - SNI (Server Name Indication). Клиент в самом начале хендшейка, в открытом виде, в ClientHello кладёт имя хоста, к которому идёт. Nginx читает SNI, по нему выбирает server-блок и его ssl_certificate, и только потом расшифровывает запрос. Поэтому несколько HTTPS-сайтов с разными сертификатами на одном IP - это норма с тех пор, как SNI стал повсеместным. Дальше, после расшифровки, Nginx ещё раз сверяет уже настоящий Host из запроса и выбирает финальный server-блок - и если SNI и Host указывают на разные виртуальные хосты, решает именно Host. Практический вывод: для HTTPS тебе нужен валидный сертификат в каждом server-блоке (или один с несколькими SAN/wildcard), а дефолтный HTTPS-сервер выбирается по тем же правилам default_server, что и для HTTP.

Практика: канонизация хоста и редирект www <-> без www

Один сайт почти всегда доступен по двум адресам: example.com и www.example.com. Для SEO и для здравого смысла нужно выбрать один канонический и редиректить на него вечным 301. Делается это отдельным маленьким server-блоком, как в эталонном проекте (https_server.conf там добавляет отдельный server www->non-www 301):

Код: Выделить всё

# Канонический сайт - без www
server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;

    root /var/www/example;
}

# Блок-редиректор: ловит www и гонит на голый домен
server {
    listen 443 ssl;
    http2 on;
    server_name www.example.com;

    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;

    return 301 https://example.com$request_uri;
}
Почему отдельным server-блоком, а не через if внутри одного? Потому что выбор server по server_name - это и есть штатный механизм Nginx, он бесплатный и не требует ни одного лишнего сравнения в рантайме. if в location - то, чего стоит избегать. Здесь $request_uri сохраняет полный путь с query-строкой, так что /catalog?id=7 переедет корректно. Обрати внимание: редиректор тоже должен иметь сертификат на www.example.com (или общий wildcard/SAN), иначе клиент упрётся в ошибку TLS ещё ДО того, как получит твой 301 - хендшейк случается раньше редиректа.

Аналогично заворачивают весь HTTP на HTTPS - ещё одним блоком на listen 80, отдающим return 301 https://$host$request_uri.

Реальная структура http/https серверов и именованные @error-локации

В боевом проекте конфиг режут на файлы: default.conf подключает upstream и http_server, отдельно лежит https_server. В эталоне http_server.conf держит listen 80 default_server плюс IPv6, structured-логи и аккуратную обработку ошибок через ИМЕНОВАННЫЕ локации:

Код: Выделить всё

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;

    error_page 404 = @error404;
    error_page 403 = @error403;
    error_page 500 502 503 504 = @error50x;

    location @error404 { internal; root /var/www/errors; rewrite ^ /404.html break; }
    location @error403 { internal; root /var/www/errors; rewrite ^ /403.html break; }
    location @error50x { internal; root /var/www/errors; rewrite ^ /50x.html break; }

    location / {
        root /var/www/html;
        try_files $uri $uri/ =404;
    }
}
Именованная локация @error404 (с собачкой) не доступна напрямую по URL - на неё нельзя зайти снаружи, в неё попадают только внутренними редиректами через error_page. Флаг internal это закрепляет. Так ты централизуешь страницы ошибок и не дублируешь их в каждом блоке. А связка listen 80 default_server + server_name _ - это та самая ловушка для ничейного трафика, которую мы обсуждали: всё, что не попало ни в один именованный сайт, обслужит этот дефолтный блок.

Типичные грабли
  • Не тот сайт по умолчанию. Нет явного default_server - дефолтным молча становится первый server в порядке загрузки файлов (а файлы из conf.d грузятся по алфавиту). Перетасовал include - сменился дефолт. Лечится явным default_server.
  • Конфликт listen ... default_server. Помечать default_server можно ТОЛЬКО ОДИН блок на пару адрес:порт. Два таких на 80-м - nginx -t падает с "a duplicate default server". Это не баг, а защита от неоднозначности.
  • http2 как флаг listen. На nginx 1.25.1 и новее это [warn], а не ошибка, поэтому многие не замечают годами. Перенеси на http2 on в server-блок.
  • Забыл IPv6. listen 80 без listen [::]:80 - и IPv6-клиенты получают connection refused, хотя сайт "работает". Дублируй listen для [::] или используй один сокет, если система это позволяет.
  • server_name _ приняли за wildcard. Оно само НЕ ловит ничего. Без default_server такой блок просто никогда не сработает.
  • Дубли server_name. Если два блока на одном listen объявляют один и тот же домен, nginx -t выдаст "conflicting server name ... ignored" и второй молча выкинет. Проверяй nginx -t.
  • Редирект www без сертификата на www. TLS-хендшейк случается раньше return 301, и клиент видит ошибку сертификата вместо твоего редиректа.
Мини-лаба: три сайта и ловушка на одном порту

Повтори руками по шагам - 10 минут.
  • Шаг 1. Создай каталоги и индексы: mkdir -p /var/www/{a,b,errors}, положи в /var/www/a/index.html строку "SITE A", в /var/www/b/index.html строку "SITE B".
  • Шаг 2. Заведи /etc/nginx/conf.d/lab.conf с тремя блоками:

Код: Выделить всё

server {
    listen 8080 default_server;
    server_name _;
    return 444;
}
server {
    listen 8080;
    server_name a.local;
    root /var/www/a;
    index index.html;
}
server {
    listen 8080;
    server_name b.local;
    root /var/www/b;
    index index.html;
}
  • Шаг 3. Проверь синтаксис и перечитай конфиг: nginx -t затем nginx -s reload.
  • Шаг 4. Постучись с правильным Host и убедись, что прилетает нужный сайт:

Код: Выделить всё

curl -s -H "Host: a.local" http://127.0.0.1:8080/   # -> SITE A
curl -s -H "Host: b.local" http://127.0.0.1:8080/   # -> SITE B
curl -si -H "Host: ghost.local" http://127.0.0.1:8080/   # -> пустой ответ, соединение оборвано (444)
  • Шаг 5. Эксперимент: убери default_server из первого блока, перезагрузи, снова стукнись с Host: ghost.local. Теперь "ничейный" запрос попадёт в ПЕРВЫЙ блок по порядку (return 444 всё ещё там - значит соединение рвётся, но уже потому что блок первый, а не потому что дефолтный). Поменяй блоки местами - и поведение изменится. Так ты руками увидишь, как работает выбор дефолта.
Контрольные вопросы
  • 1. По каким двум вещам Nginx в первую очередь отбирает кандидатов на server-блок ещё до сравнения server_name?
  • 2. Чем server_name _ отличается от server_name *.example.com и почему _ сам по себе ничего не ловит?
  • 3. Какой правильный синтаксис включения HTTP/2 в 2026 году и почему listen ... http2 даёт [warn]?
  • 4. Как из заголовка Host (он зашифрован) Nginx понимает, чей TLS-сертификат предъявить на HTTPS, и что решает финальный выбор server - SNI или Host?
Итог

Виртуальный хост в Nginx - это server-блок, выбираемый по паре (listen-адрес:порт) и заголовку Host через server_name. Сначала фильтр по listen, потом матч по имени с приоритетом точное > wildcard > regex, а всё непойманное идёт в default_server (явный или первый по порядку). HTTPS добавляет SNI, чтобы выбрать сертификат до расшифровки. www-канонизацию делаем отдельным server-блоком с return 301, а не через if. И главное на 2026: http2 on - отдельной директивой, флаг listen http2 мёртв. Дальше будем разбирать location и маршрутизацию внутри уже выбранного хоста.
👍3 ❤️3 🔥 😄 🤔4
Аватара пользователя
lmatheo
Сообщения: 1
Зарегистрирован: 02 июн 2026, 12:10

Re: Виртуальные хосты: server и server_name

Сообщение lmatheo »

Спасибо, наконец дошло почему мой второй сайт отдавал чужой контент - не было default_server и первый блок всё перехватывал. Поставил явную ловушку с server_name _ и return 444, порядок навёлся.
👍 ❤️3 🔥1 😄 🤔
Аватара пользователя
mitsu1
Сообщения: 1
Зарегистрирован: 19 май 2026, 00:36

Re: Виртуальные хосты: server и server_name

Сообщение mitsu1 »

Вопрос: а если у меня wildcard сертификат на *.example.com, мне всё равно нужен отдельный server-блок для редиректа www на голый домен, или хватит одного? И SNI тут что отдаёт - голый домен или www?
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Анатомия конфигурации: контексты, наследование и модульность
Следующая глава →
Директива location: матчинг и приоритеты по шагам

Все главы курса «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Поделиться темой: ✈ Telegram VK
Похожие запросы: Раздача статики и SPA на nginxДиректива location в nginx: матчинг и приоритетыВиртуальные хосты nginx: server_name

Вернуться в «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость