Отключение декодирования URL в прокси nginx

21

Когда я просматриваю этот URL: http://localhost:8080/foo/%5B-%5Dserver ( nc -l 8080) получает его как есть:

GET /foo/%5B-%5D HTTP/1.1

Однако, когда я прокси это приложение через nginx (1.1.19):

location /foo {
        proxy_pass    http://localhost:8080/foo;
}

Тот же запрос, перенаправленный через порт nginx, пересылается с декодированным путем:

GET /foo/[-] HTTP/1.1

Декодированные квадратные скобки в пути GET вызывают ошибки на целевом сервере ( HTTP Status 400 - Недопустимый символ в пути ... ), поскольку они поступают без экранирования.

Есть ли способ отключить декодирование URL или закодировать его обратно, чтобы целевой сервер получал точно такой же путь при маршрутизации через nginx? Какое умное правило перезаписи URL?

Томаш Нуркевич
источник
Сообщается об ошибке в nginx: trac.nginx.org/nginx/ticket/262
Томаш Нуркевич

Ответы:

19

Цитируя Валентина В. Бартенева (который должен получить полную оценку за этот ответ):

Цитата из документации :

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

  • Если proxy_passуказан без URI , URI запроса передается на сервер в той же форме, в которой он был отправлен клиентом при обработке исходного запроса

Правильная конфигурация в вашем случае будет:

location /foo {
   proxy_pass http://localhost:8080;
}
Томаш Нуркевич
источник
8
Я должен был измениться http://localhost:8080/на http://localhost:8080случай, если у кого-то будет такая же ситуация, как и у меня.
Herrtim
4
Почему Nginx декодирует URI перед передачей его на внутренний сервер? Разве не было бы больше смысла, если бы он оставил URI нетронутым?
утконос
@platypus, он остается без изменений, пока вы явно не начнете выполнять замены
cnst
2

Обратите внимание, что декодирование URL, обычно известное как $uri«нормализация» в документации по nginx, происходит до IFF бэкэнда:

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

  • или URI изменяется во время обработки, например, через rewrite.


Оба условия четко задокументированы на http://nginx.org/r/proxy_pass (выделено мной):

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

  • Если proxy_passуказано без URI , URI запроса передается на сервер в той же форме, что и отправленный клиентом при обработке исходного запроса, или полный URI нормализованного запроса при обработке измененного URI


Решение состоит в том, чтобы либо опустить URI, как в случае с OP, либо использовать действительно умное rewriteправило:

# map `/foo` to `/foo`:
location /foo {
    proxy_pass  http://localhost:8080;  # no URI -- not even just a slash
}

# map `/foo` to `/bar`:
location /foo {
    rewrite  ^  $request_uri;            # get original URI
    rewrite  ^/foo(/.*)  /bar$1  break;  # drop /foo, put /bar
    return 400;   # if the second rewrite won't match
    proxy_pass    http://localhost:8080$uri;
}

Вы можете увидеть его в реальном времени в ответе о переполнении стека , включая контрольную группу.

CNST
источник
Документация сбивает с толку здесь. Обе формы содержат URI. Это компонент пути, который присутствует в одном и отсутствует в другом.
Майкл Хэмптон
@MichaelHampton, я не согласен - PATH обычно называют URI, поэтому тот, у которого нет пути, не содержит URI.
CNST
Разумеется, только относительный путь может быть допустимым URL-адресом. Дело в том, что остаток также является допустимым URI (например http://localhost:8080). Если вы не согласны, вы можете взять его с авторами RFC 3986.
Michael Hampton
@MichaelHampton К сожалению, кажется, что схема и путь являются обязательными, чтобы быть URI, полномочия, аргументы, фрагмент необязательны
Norman Xu