Я использую 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
из содержащих декодированный запрос, но все же выполнять всевозможные преобразования запроса.
Идея в том, что когда вы меняете $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-доступа), сообщите мне!