Домены
Фреймворк поддерживает виртуальные хосты — несколько доменов на одном сервере (или несколько серверов на одном ip:port). Домены задаются массивом строк и могут быть точными именами, содержать подстановочные знаки * или полноценные регулярные выражения.
"servers": {
"s1": {
"domains": [
"example.com",
"www.example.com",
"*.example.com",
"mail.*",
"(api|www).example.com"
],
"ip": "0.0.0.0",
"port": 80,
...
},
...
}Как происходит сопоставление
Виртуальный сервер выбирается по значению HTTP-заголовка Host. Перед сравнением выполняется нормализация:
- Порт отсекается — из
example.com:8080остаётсяexample.com. - IPv6-литералы обрабатываются по RFC 3986: скобки
[::1]:8080снимаются, для сравнения берётся сам адрес::1. - IDN-преобразование — домен в UTF-8 (например,
пример.рф) конвертируется в Punycode (xn--80akhbyknj4f.xn--p1ai), см. Интернационализированные домены (IDN).
Затем значение поочерёдно сравнивается с доменами каждого сервера, привязанного к тому же ip:port. Совпало первым — выиграло: приоритет между точным именем, wildcard и регулярным выражением отсутствует, порядок определяется только последовательностью в массиве domains. Поэтому более специфичные имена следует размещать выше общих.
Порядок имеет значение
Поскольку *.example.com захватывает и www.example.com, и более глубокие адреса, размещайте точные имена и узкие шаблоны выше wildcard, чтобы они не «перекрывались».
Если подходящий сервер не найден, клиент получит 404 Not Found. Пустой или отсутствующий заголовок Host (он обязателен в HTTP/1.1) приводит к 400 Bad Request.
Подстановочные имена
Звёздочка * допускается только в начале или в конце имени и раскрывается в регулярное выражение .*:
"domains": [
"*.example.com",
"mail.*"
]*.example.comматчит какwww.example.com, так иwww.sub.example.com— звёздочка захватывает сразу несколько уровней.mail.*матчитmail.com,mail.orgи тому подобное.- Имена вида
w*.example.comилиwww.*.example.orgнедопустимы — звёздочка в середине вызывает ошибку загрузки конфигурации. Такие случаи реализуются регулярным выражением (см. ниже).
Внутри скобочных групп (...) и [...] символы * и . сохраняют regex-смысл (квантификатор и «любой символ» соответственно) и не подвергаются автопреобразованию.
Регулярные выражения
В качестве имени домена можно использовать регулярное выражение, совместимое с PCRE (Perl Compatible Regular Expressions):
"domains": [
"(api|www).example.com",
"^www(\\d+).example.net$",
"(.1|.*|a3).example.com"
]Особенности обработки:
- Точки экранируются автоматически. Символ
.вне скобок превращается в\., поэтомуexample.comможно писать как есть, без экранирования. Внутри(...)/[...].сохраняет смысл «любой символ». - Автоматическое якорение. Если выражение не содержит ни
^в начале, ни$в конце, шаблон автоматически оборачивается в^...$(полное совпадение). Указав хотя бы один из якорей, вы полностью отключаете авто-якорение. - Чувствительность к регистру. Сопоставление регистрозависимо; браузеры обычно отправляют
Hostв нижнем регистре.
Интернационализированные домены (IDN)
Поддержка кириллических и иных национальных доменов реализована через libidn2. Домены в конфигурации можно задавать в UTF-8 — при загрузке они конвертируются в Punycode, и то же преобразование применяется к заголовку Host (и TLS SNI) во время сравнения:
"domains": [
"пример.рф",
"*.пример.рф"
]Если Host содержит некорректный IDN, который невозможно преобразовать, клиент получит 404 Not Found.
TLS и SNI
На TLS-соединениях виртуальный сервер первоначально выбирается по SNI (Server Name Indication). Как SNI, так и последующий заголовок Host проходят IDN-преобразование и сравниваются с одними и теми же шаблонами.
Согласно RFC 9110, на TLS-соединении с SNI заголовок Host обязан соответствовать серверу, выбранному по SNI. Если они расходятся — клиент получит 404 Not Found.
Краткая справка
| Ситуация | Ответ |
|---|---|
Пустой или отсутствующий Host (HTTP/1.1) | 400 Bad Request |
| Ни один домен не совпал | 404 Not Found |
Host не соответствует серверу, выбранному по SNI (TLS) | 404 Not Found |
Некорректный IDN в Host | 404 Not Found |