Как переслать все параметры URL-адреса через proxy_pass с помощью nginx?

#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, но я им не пользовался и не могу привести вам пример.