Рейтинг:0

Использование части URI в качестве номера порта для директивы nginx proxy_pass

флаг cn

Это моя конфигурация nginx:

сервер {
        слушать 443 ssl;
        имя_сервера sub.example.fr ;
        location ~ ^/ (123[0-9])$ { # работа с регулярными выражениями
                #rewrite ^/[0-9]{4}(.*)$ $1 last; # не работает, с last или break
                #proxy_pass http://localhost:$1/; # добавить косую черту не разрешается
                proxy_pass http://localhost:$1;
        }
}

я хочу переслать https://sub.example.fr/1234 к http://локальный:1234, поэтому я просто хочу извлечь номер порта, удалить его из URL-адреса и использовать в proxy_pass.

Рейтинг:2
флаг gr

Прежде всего, ваш ^/(123[0-9])$ регулярное выражение будет соответствовать только /1234 URI (или /1230, /1231и др.), но не /1234/некоторые/путь так как вы используете $ якорь на конце струны. Я предполагаю, что это не ошибка, а дизайнерское решение. Если это не так, чтобы соответствовать обоим /1234 и /1234/некоторые/путь (но не что-то вроде /12345), вы можете использовать чередование: ^/(123[0-9])(?:/|$) (Я использую группу без захвата (?:...) здесь, так как считается, что он имеет немного лучшую производительность, чем группа захвата).

В вашей конфигурации есть целая куча различных ошибок. Давайте рассмотрим каждый.

Ошибка №1: Неправильно переписать... последний; Применение.

Действительно, вы не можете указать URI для proxy_pass вверх по течению, как вы пытаетесь сделать с косой чертой

proxy_pass http://localhost:$1/; # добавить косую черту не разрешается

внутри местоположений, совпадающих с регулярными выражениями (а также внутри именованных местоположений), и единственный способ изменить URI, который будет передан серверной части, — это использовать переписать директива. Однако правильный способ изменить URI внутри место расположения блок для обработки его позже в том же месте, чтобы использовать переписать... сломать; поскольку переписать... последний; заставит nginx искать новое место для переписанного URI. Это означает, что мы должны использовать сломать флаг для переписать директива:

переписать ^/[0-9]{4}(.*)$ $1 break;

Остерегаться! Никаких указаний от ngx_http_rewrite_module будет выполнен после переписать... сломать; (или просто сломать;) директива (Обновить: после некоторого тестирования, вопреки тому, что сказано в документация, оказывается верно только для сломать директива, но не переписать... сломать один, по крайней мере, для OpenResty 1.17.8.2 на базе ядра nginx 17.8).

Ошибка №2: Вы должны заключать в кавычки любую строку, содержащую фигурные скобки, иначе они будут считаться блоком конфигурации nginx.

Предыдущая директива выдаст нам следующую ошибку:

nginx: директива [emerg] «rewrite» не завершается символом «;»

Чтобы избавиться от этой ошибки, нам нужно заключить наш шаблон регулярного выражения в кавычки из-за использования фигурных скобок:

переписать "^/[0-9]{4}(.*)$" $1 break;

Ошибка № 3: пронумерованные захваты перезаписываются всякий раз, когда вычисляется регулярное выражение.

Предположим, у вас есть URI /1234:

расположение ~ ^/(123[0-9])$ {
    # Здесь значение переменной '$1' равно "1234"
    переписать "^/[0-9]{4}(.*)$" $1 break;
    # Здесь значение переменной '$1' является пустой строкой!
    proxy_pass http://localhost:$1; # Не будет порта для директивы proxy_pass
}

Вы можете сохранить это $1 значение перед тем, как правило перезаписи вычислит собственное регулярное выражение, используя установлен директива:

расположение ~ ^/(123[0-9])$ {
    установить $порт $1;
    переписать "^/[0-9]{4}(.*)$" $1 break;
    proxy_pass http://localhost:$port;
}

или лучше использовать именованная группа захвата:

расположение ~ ^/(?<порт>123[0-9])$ {
    переписать "^/[0-9]{4}(.*)$" $1 break;
    proxy_pass http://localhost:$port;
}

Иногда не может быть другого решения, кроме как использовать именованные захваты, отличный пример такой ситуации приведен на это нить.

Ошибка №4: Переписанный URI не может быть пустой строкой.

Опять же, давайте предположим, что у вас есть URI /1234. После правила перезаписи внутренний nginx $ури переменная будет иметь пустое значение. Это приведет к ошибке внутреннего сервера HTTP 500, а в журнале ошибок nginx вы увидите следующее сообщение об ошибке:

перезаписанный URI имеет нулевую длину

Чтобы устранить эту ошибку, просто перепишите любой URI на /:

расположение ~ ^/(?<порт>123[0-9])$ {
    переписать ^/перерыв;
    proxy_pass http://localhost:$port;
}

Если вам нужно служить любой /<номер_порта>/некоторые/путь запроса, как указано выше, вы должны использовать следующий блок местоположения, чтобы ваш переписанный URI начинался с косой черты:

расположение ~ ^/(?<порт>123[0-9])(?:/|$) {
    переписать "^/\d{4}(?:/(.*))?" /$1 перерыв;
    proxy_pass http://localhost:$port;
}

Ошибка № 5: вам нужно указать преобразователь при использовании переменных с proxy_pass директива.

Приведенная выше конфигурация близка к рабочему решению. Однако он по-прежнему не работает, что приводит к ошибке HTTP 502 Bad Gateway. Вам необходимо указать резольвер когда вы используете переменные с proxy_pass директива и ваш апстрим указывается именем домена, а не IP-адресом, иначе вы получите следующую ошибку в журнале ошибок nginx:

не определен преобразователь для разрешения localhost

Как правило, вам придется использовать что-то вроде

резольвер 8.8.8.8;
расположение ~ ^/(?<порт>123[0-9])$ {
    переписать ^/перерыв;
    proxy_pass http://localhost:$port;
}

но так как ваш восходящий поток локальный хост, вы можете указать его IP-адрес вместо имени домена, тогда вам это не понадобится. резольвер директива вообще:

расположение ~ ^/(?<порт>123[0-9])$ {
    переписать ^/перерыв;
    proxy_pass http://127.0.0.1:$port;
}

Эта конфигурация должна быть полностью работоспособной (наконец-то). И еще раз, если вам нужно будет служить какой-либо /<номер_порта>/некоторые/путь запрос, как указано выше, вместо этого вы должны использовать следующий блок местоположения:

расположение ~ ^/(?<порт>123[0-9])(?:/|$) {
    переписать "^/\d{4}(?:/(.*))?" /$1 перерыв;
    proxy_pass http://127.0.0.1:$port;
}
themadmax avatar
флаг cn
Большое спасибо, ваш ответ действительно впечатляет и очень поучителен, надеюсь, он поможет другим людям!
Ivan Shatsky avatar
флаг gr
Я сделал несколько (надеюсь, полезных) обновлений ответа. Да, ответ получился довольно подробным (не стоит ли проголосовать за? :) ), поэтому, чтобы помочь другим людям найти эту информацию, вы можете переименовать свой вопрос во что-то более распространенное, например _Использовать часть URI как номер порта для директивы nginx proxy_pass_ или, что еще более распространено, _использование части URI с директивой nginx proxy_pass_.
флаг us
Можно также захватить все элементы в директиве `location` и пропустить `rewrite`.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.