Внешние ссылки URL-кодирование приводит к "% 3F" и "% 3D" на сервере Nginx

У меня проблема с моим сервером. Я получил четыре входящие ссылки на разные сайты моей динамической веб-страницы, которые выглядят примерно так:

myurl.com/default/Site%3Fid%3D13

Они должны выглядеть так:

myurl.com/default/Site?id=13

Я знаю, что те %3F - это escape-последовательность для знака ?, а %3D - escape-последовательность для знака равенства. Но я получаю ошибку 400, когда я использую эти ссылки. Что я могу сделать с этим?

Четыре ссылки предназначены для разных сайтов, и я предполагаю, что со временем появится больше таких ссылок. Таким образом, одно исправление для всех было бы идеальным.

Ответ 1

Точно такой же вопрос был задан в списке рассылки nginx-ru около года назад:

http://mailman.nginx.org/pipermail/nginx-ru/2013-February/050200.html

Самый полезный ответ от Nginx, Inc, сотрудник/разработчик, Валентин Бартенев:

http://mailman.nginx.org/pipermail/nginx-ru/2013-February/050209.html

Если запрос приходит в таком виде, то это уже не параметры, а имя запрошенного файл. Другое дело, что местоположение ищется по уже раскодированному адресу, о чем в документации написано.

Перевод:

Если запрос приходит в такой форме, то это уже не аргументы, а имя запрашиваемого файла. Другое дело, что, как задокументировано, сопоставление местоположений выполняется против нормализованного URI.

Его предлагаемое решение, переведенное на примерный пример из вопроса здесь, в SO, было бы следующим:
location /default/Site? {
    rewrite \?(.*)$ /default/Site?$1? last;
}

location = /default/Site {
    [...]
}

Ответ 2

Следующий пример будет перенаправлять все ошибочно выглядящие запросы (определяемые как имеющие ? в запрошенном имени файла — закодированные как %3F в запросе) в менее ошибочные, независимо от URL.

(Обратите внимание, что, как правильно сказано в другом месте, вы не должны получать эти неправильно сформированные ссылки в первую очередь, поэтому используйте его как последнее средство - только тогда, когда вы не можете исправить неправильно сформированные ссылки в противном случае и вы знаете, что такие запросы предпринимаются действительными агентами.)

server {
    listen      [::]:80;
    server_name localhost;

    rewrite     ^/([^?]*)\?(.*)$    /$1?$2?     permanent;
    location / {
        return  200 "id is $arg_id\n";
    }
}

Это пример того, как это будет работать:— когда встречается ошибочно выглядящий запрос, попытка коррекции выполняется с ответом 301 Moved Permanently с якобы правильным заголовком ответа Location, который заставит браузер автоматически повторно отправить запрос в только что предоставленное местоположение:

opti# curl -6v "http://localhost/default/Site%3Fid%3D13"
* About to connect() to localhost port 80 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 80 (#0)
> GET /default/Site%3Fid%3D13 HTTP/1.1
> User-Agent: curl/7.26.0
> Host: localhost
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.4.1
< Date: Wed, 15 Jan 2014 17:09:25 GMT
< Content-Type: text/html
< Content-Length: 184
< Location: http://localhost/default/Site?id=13
< Connection: keep-alive
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.4.1</center>
</body>
</html>
* Connection #0 to host localhost left intact
* Closing connection #0

Обратите внимание, что попытки исправления не выполняются по соответствующим запросам:

opti# curl -6v "http://localhost/default/Site?id=13"
* About to connect() to localhost port 80 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 80 (#0)
> GET /default/Site?id=13 HTTP/1.1
> User-Agent: curl/7.26.0
> Host: localhost
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.4.1
< Date: Wed, 15 Jan 2014 17:09:30 GMT
< Content-Type: application/octet-stream
< Content-Length: 9
< Connection: keep-alive
<
id is 13
* Connection #0 to host localhost left intact
* Closing connection #0

Ответ 3

URL отлично действует. Экранированные символы, которые он содержит, - это просто бегство. Это прекрасно.

Цель состоит в том, что на самом деле у вас может быть имя запроса (в большинстве случаев соответствующее имени файла на диске), то есть Site?id=13, а не Site, а остальное как строка запроса.

Я считаю неправильной практикой иметь символы в имени файла, что делает это необходимым. Однако в аргументах URL это может быть очень необходимо.

Тем не менее, URL-адрес запроса действителен и, вероятно, не тот, кем вы его хотите. Это, следовательно, предполагает, что вы должны исправить ошибку, если кто-то взял неправильный URL-адрес в первую очередь.

Я действительно не понимаю, почему вы получаете ошибку 400; вы должны скорее получить ошибку 404. Но это зависит от вашей настройки.

Также есть случаи, особенно с nginx, которые в основном включают передачу целых URL-адресов и URL-адресов на нескольких уровнях (например, обратные прокси, сопоставление регулярных выражений из URL-адреса и использование их в качестве переменных и т.д.), где такая ошибка может возникнуть. Но чтобы проверить это и исправить, нам нужно будет узнать больше о вашей настройке.