Назад | Перейти на главную страницу

nginx $ uri декодируется по URL

Я использую nginx как обратный прокси, и у меня есть 2 правила, например:

location ~ ^/indirect {
  rewrite ^/indirect(.*) /foobar$1;
}

location ~ ^/foobar {
  set $url http://example.com/something/index.php?var1=hello&access=$scheme://$host$uri;
  proxy_pass $url;
}

Итак, как видите, я прохожу мимо $uri переменная в качестве параметра проксируемой страницы ( $uri переменная nginx, см. http core module документация).

Проблема в том, что если я приду http://example.com/foobar/hello%20world, то $uri переменная содержит /foobar/hello world (как видите, %20 был заменен его значением, декодированным в URL-адресе, пробелом). Затем nginx возвращает код статуса http 400 (неверный запрос) перед выполнением строки proxy_pass (с сервером не связывается).

Также доступна переменная $request_uri, который содержит исходный URI запроса, выданный клиентом, поэтому в этом случае он будет содержать правильное значение с %20 последовательность. Но я не могу использовать это, потому что если клиент проходит через /indirect дорожка, $request_uri будет содержать /indirect/... пока я хочу, чтобы access param передается на бэкэнд всегда /foobar/....

Есть несколько indirect-подобные правила (это для сервера DAV / calDAV / cardDAV, и есть несколько клиентов, которые подключаются к нескольким путям, поэтому мне нужны эти indirect-подобные правила), поэтому выполнить proxy_pass там, а есть клиенты, которые идут прямо в /foobar дорожка.

Так есть ли способ получить $uri без url-декодирования?

возможные неприемлемые вещи:

С участием nginx/1.2.1, Мне не удалось воспроизвести вашу проблему %20после декодирования в пространство, вызывая любые 400 Bad Request внутри nginx; возможно это исходит из апстрима?

Тем не менее, на самом деле не так сложно использовать конечный автомат, который предоставляется через rewrite директива остановить $uri из содержащих декодированный запрос, но все же выполнять всевозможные преобразования запроса.

https://stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637

Идея в том, что когда вы меняете $uri на месте, он не декодируется повторно. И, как вы знаете, у нас уже есть недекодированный код в $request_uri. Осталось просто установить одно на другое и закончить.

server {
    listen 2012;
    location /a {
        rewrite ^/a(.*) /f$1 last;
    }
    location /i {
        rewrite ^ $request_uri;
        rewrite ^/i(.*) /f$1 last;
        return 400; #if the second rewrite won't match
    }
    location /f {
        set $url http://127.0.0.1:2016/s?v=h&a=$scheme://$host$uri;
        proxy_pass $url;
    }
}

server {
    listen 2016;
    return 200 $request_uri\n;
}

И да, rewrite ^ $request_uri; часть выше делает трюк:

% echo localhost:2012/{a,i,f}/h%20w | xargs -n1 curl
/s?v=h&a=http://localhost/f/h w
/s?v=h&a=http://localhost/f/h%20w
/s?v=h&a=http://localhost/f/h w
%

(Если вы хотите, чтобы и «прямой» объект не декодировался, тогда, вероятно, будет проще просто сделать его «косвенным».)

Я нашел единственный способ - использовать HttpSetMiscModule вот так:

location ~
 ^/indirect {
  set_escape_uri $key $1;
  rewrite ^/indirect(.*) /foobar$key;
}

location ~ ^/foobar {
  set_escape_uri $key $uri;
  set $url http://example.com/something/index.php?var1=hello&access=$scheme://$host$key;
  proxy_pass $url;
}

Если кто-то знает лучший способ (без необходимости компилировать nginx с внешними модулями, потому что у меня нет root-доступа), сообщите мне!