#nginx #parameters #nginx-config #proxypass
Вопрос:
Как переслать все параметры URL-адреса через proxy_pass с помощью nginx?
Конфигурация Nginx:
location /proxy/ {
if ($request_method = HEAD) { return 200; }
if ( $arg_address != "" ) {
proxy_pass $arg_address;
return 301 $arg_address;
}
proxy_ssl_verify off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
этот URL-адрес работает:
https://localhost/proxy/?address=https://exemple.com/transfer/file.txt = = > > > >> > https://exemple.com/transfer/file.txt
или
https://localhost/proxy/?address=https://exemple.com/transfer/file.txt?host-id=1 = = > > > >> > https://exemple.com/transfer/file.tx?host-id=1
если я добавлю несколько параметров, они будут усечены до первого»amp;».
https://localhost/proxy/?address=https://exemple.com/transfer/file.txt?host-id=1amp;password=123456amp;date=xxxxxx = = > > > >> > https://exemple.com/transfer/file.txt?host-id=1
Как я могу перенести весь URL-адрес целиком?
Ответ №1:
Прежде чем мы перейдем к ответу, могу я спросить, чего вы хотите достичь? Вы хотите выполнить прокси-запрос или создать перенаправление HTTP 301? Со следующей конструкцией
if ( $arg_address != "" ) {
proxy_pass $arg_address;
return 301 $arg_address;
}
вы всегда будете получать перенаправление, потому что директивы от ngx_http_rewrite_module
выполняются раньше любых других, поэтому proxy_pass
директива здесь бесполезна. ngx_http_rewrite_module
является очень особенным и отличается от большинства других модулей. Хотя конфигурация nginx в целом декларативна, модуль перезаписи оценивает свои инструкции императивно. Это всегда является источником путаницы для каждого новичка nginx. Вы можете прочитать больше о внутренней реализации модуля перезаписи здесь.
Если вы хотите выполнить прокси-запрос вместо создания перенаправления, вам нужно удалить return
и добавить resolver
директиву в свою конфигурацию. Здесь вы можете прочитать, почему это необходимо.
Решение «грязного взлома»
С учетом сказанного, вернемся к вопросу. Конечно, когда nginx получит запрос
https://localhost/proxy/?address=https://example.com/transfer/file.txt?host-id=1amp;password=123456amp;date=20210520
arg_NAME
переменные будут заполнены следующим образом:
arg_address => https://example.com/transfer/file.txt?host-id=1
arg_password => 123456
arg_date => 20210520
Это правильное и ожидаемое поведение.
Что вы можете сделать, чтобы сохранить все остальные аргументы запроса? Самое простое — предположить, что все аргументы запроса, следующие за address
одним, подлежат передаче в вышестоящий поток, и использовать map
директиву для получения требуемой строки:
map $args $address {
~(?:^|amp;)(address=.*) $1;
}
server {
...
location /proxy/ {
...
if ($address) {
# 'proxy_pass $address' or 'return 301 $address' here
}
...
}
...
}
Здесь более строгая проверка, где мы берем остальную часть строки запроса только в том случае, если после address
параметра запроса стоит знак вопроса и только $arg_address
значение в противном случае:
map $args $address {
~(?:^|amp;)(address=[^amp;?] ?.*) $1;
default $arg_address;
}
Надежное решение
Хотя приведенный выше ответ в целом выполним, я бы предпочел попытаться разработать свое прокси-решение, используя кодировку URL в address
аргументе запроса, чтобы избежать использования зарезервированных символов в качестве части значения аргумента запроса. Приведенный выше запрос, закодированный по URL-адресу, будет выглядеть следующим образом
https://localhost/proxy/?address=https://example.com/transfer/file.txt?host-id=1&password=123456&date=20210520
Плохо то, что «ванильный» nginx не имеет возможности декодировать URL-адрес произвольной строки. Однако это можно сделать с помощью OpenResty/lua-nginx-модуля:
location /proxy/ {
...
set_by_lua_block $address { return ngx.unescape_uri(ngx.var.arg_address) }
if ($address) {
# 'proxy_pass $address' or 'return 301 $address' here
}
...
}
или set-misc-nginx-module
:
location /proxy/ {
...
if ($arg_address) {
set_unescape_uri $address $arg_address;
# 'proxy_pass $address' or 'return 301 $address' here
}
...
}
Возможно, то же самое можно сделать с помощью njs, но я им не пользовался и не могу привести вам пример.