Используйте X-Forwarded-Host в server_name для nginx

Вопрос или проблема

Как я могу повторно использовать переменную 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-сокетов можно значительно улучшить производительность и функциональность вашего веб-сервера. При правильной настройке вы сможете избежать проблем с заголовками и поддерживать высокую доступность и производительность вашего сервиса.

Оцените материал
Добавить комментарий

Капча загружается...