Вопрос или проблема
Как я могу повторно использовать переменную X-Forwarded-Host
в качестве аргумента для server_name
?
Никак. Извините. Имена серверов используются для создания хеш-таблицы при запуске nginx, где поиск необходимого блока сервера по значению заголовка Host
будет происходить с временной сложностью O(1)
. Имена серверов, указанные с помощью регулярных выражений, – это другая история, такие регулярные выражения будут соответствовать значению заголовка Host
, если поиск в хеш-таблице не удался. Тем не менее, вы не можете использовать переменные в регулярных выражениях, все регулярные выражения компилируются при запуске nginx. Если вы опишете задачу, которую пытаетесь решить, возможно, я смогу предложить вам другой способ ее решения.
Обновление в соответствии с комментарием ОП:
Ну, URL, который передается в наш nginx, преобразуется в локальный путь, где домен удаляется. Единственный способ восстановить фактический домен – из заголовка
X-Forwarded-Host
.
Если я правильно вас понимаю, вы можете попробовать проксировать входящие запросы на свой экземпляр nginx, используя полученное значение заголовка X-Forwarded-Host
в качестве значения заголовка Host
для исходящего запроса. Минимальный пример выглядит следующим образом:
server {
listen 80;
location / {
proxy_set_header Host $http_x_forwarded_host;
proxy_pass 127.0.0.1:8080;
}
}
server {
listen 127.0.0.1:8080;
server_name one.example.com;
location / {
return 200 "Сервер один, URI запроса - $request_uri\n";
}
}
server {
listen 127.0.0.1:8080;
server_name two.example.com;
location / {
return 200 "Сервер два, URI запроса - $request_uri\n";
}
}
Тестирование:
> curl -H 'X-Forwarded-Host: one.example.com' http://127.0.0.1/some/path/
Сервер один, URI запроса - /some/path/
> curl -H 'X-Forwarded-Host: two.example.com' http://127.0.0.1/other/path/?arg=value
Сервер два, URI запроса - /other/path/?arg=value
Поскольку nginx проксирует запросы на себя, может быть более эффективно использовать UNIX-сокет вместо TCP-порта для связи. Этот подход устраняет накладные расходы сетевого стека, что приводит к несколько лучшей производительности:
server {
listen 80;
location / {
proxy_set_header Host $http_x_forwarded_host;
proxy_pass http://unix:/tmp/nginx.sock;
}
}
server {
listen http://unix:/tmp/nginx.sock;
server_name one.example.com;
...
}
server {
listen http://unix:/tmp/nginx.sock;
server_name two.example.com;
...
}
...
Если вам нужно использовать версию протокола HTTP/1.1, где заголовок запроса Host
не может быть пустым согласно спецификации (в противном случае это приведет к ошибке HTTP 400 Bad Request
), чтобы гарантировать, что каждый ваш проксированный запрос будет иметь заголовок Host
, вы можете использовать следующий блок map
:
map $http_x_forwarded_host $proxied_host {
'' stub.example.com;
default $http_x_forwarded_host;
}
server {
listen 80;
location / {
proxy_http_version 1.1;
proxy_set_header Host $proxied_host;
proxy_pass http://unix:/tmp/nginx.sock;
}
}
server {
# блок сервера для обработки запросов без HTTP-заголовка `X-Forwarded-Host`
listen http://unix:/tmp/nginx.sock;
server_name stub.example.com;
...
}
...
Ответ или решение
Вопрос, связанный с использованием заголовка X-Forwarded-Host
в параметре server_name
для сервера Nginx, требует более детального анализа особенностей работы Nginx и его конфигурации. Важно отметить, что переменные в конфигурации server_name
использовать нельзя, так как это приведет к проблемам с производительностью и структурой самой системы.
Проблема и ограничения
В отличие от других конфигурационных параметров, server_name
используется для создания хэш-таблицы при запуске Nginx. Это позволяет осуществлять быстрый поиск нужного блока сервера по значению заголовка Host
с временной сложностью O(1)
. Паттерны regex
в значении server_name
обрабатываются на этапе компиляции, а не в процессе обработки запросов, что еще раз подтверждает невозможность использования переменных для маршрутизации запросов.
Решение
Учитывая, что требуется извлекать фактическое имя домена из заголовка X-Forwarded-Host
, можно использовать несколько подходов для достижения желаемого результата. Один из наиболее эффективных методов заключается в проксировании входящих запросов на другой экземпляр Nginx, использовать заголовок X-Forwarded-Host
как значение заголовка Host
в выдаваемом запросе. Приведём пример настройки:
server {
listen 80; # Слушаем на порту 80
location / {
proxy_set_header Host $http_x_forwarded_host; # Устанавливаем заголовок Host
proxy_pass http://127.0.0.1:8080; # Проксируем запрос на локальный экземпляр
}
}
server {
listen 127.0.0.1:8080; # Локально слушаем на порту 8080
server_name one.example.com; # Указываем имя сервера
location / {
return 200 "Сервер один, URI запроса: $request_uri\n"; # Ответ для домена one.example.com
}
}
server {
listen 127.0.0.1:8080; # Локально слушаем на порту 8080
server_name two.example.com; # Указываем имя второго сервера
location / {
return 200 "Сервер два, URI запроса: $request_uri\n"; # Ответ для домена two.example.com
}
}
Тестирование
Для проверки работоспособности настройки можно использовать утилиту curl
:
curl -H 'X-Forwarded-Host: one.example.com' http://127.0.0.1/some/path/
# Ожидаемый вывод: Сервер один, URI запроса: /some/path/
curl -H 'X-Forwarded-Host: two.example.com' http://127.0.0.1/other/path/?arg=value
# Ожидаемый вывод: Сервер два, URI запроса: /other/path/?arg=value
Оптимизация производительности
Для улучшения производительности и снижения накладных расходов на сеть можно использовать UNIX-сокеты вместо TCP-портов для связи между серверами:
server {
listen 80;
location / {
proxy_set_header Host $http_x_forwarded_host;
proxy_pass http://unix:/tmp/nginx.sock; # Используем UNIX-сокет
}
}
server {
listen http://unix:/tmp/nginx.sock; # Настройка для первого сервера
server_name one.example.com;
...
}
server {
listen http://unix:/tmp/nginx.sock; # Настройка для второго сервера
server_name two.example.com;
...
}
Обработка отсутствующего заголовка
Если существует вероятность, что заголовок X-Forwarded-Host
будет отсутствовать, можно использовать map
, чтобы задать значение по умолчанию:
map $http_x_forwarded_host $proxied_host {
'' stub.example.com; # Значение по умолчанию
default $http_x_forwarded_host; # Используем заголовок, если он есть
}
server {
listen 80;
location / {
proxy_http_version 1.1;
proxy_set_header Host $proxied_host; # Устанавливаем заголовок Host
proxy_pass http://unix:/tmp/nginx.sock;
}
}
server {
listen http://unix:/tmp/nginx.sock; # Обслуживаем отсутствующий заголовок
server_name stub.example.com;
...
}
Заключение
Итак, хотя непосредственно использовать X-Forwarded-Host
в server_name
невозможно, существуют эффективные подходы для использования этого заголовка в конфигурации Nginx. С помощью проксирования и применения UNIX-сокетов можно значительно улучшить производительность и функциональность вашего веб-сервера. При правильной настройке вы сможете избежать проблем с заголовками и поддерживать высокую доступность и производительность вашего сервиса.