避免在proxy_pass上使用nginx解码查询参数(相当于AllowEncodedSlashes NoDecode)

时间:2013-12-10 14:22:47

标签: nginx load-balancing reverse-proxy

我在几个tomcats面前使用nginx作为负载balencer。在我的传入请求中,我编码了查询参数。但是当请求到达tomcat时,参数将被解码:

传入nginx的请求:

curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http%3A%2F%2Fwww.google.com%2F"

传入tomcat的请求:

curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http:/www.google.com/"

我不希望我的请求参数被转换,因为在那种情况下我的tomcat会抛出405错误。

我的nginx配置如下:

upstream tracking  {
    server front-01.server.com:8080;
    server front-02.server.com:8080;
    server front-03.server.com:8080;
    server front-04.server.com:8080;
}

server {
    listen 80;
    server_name tracking.server.com;
    access_log /var/log/nginx/tracking-access.log;
    error_log  /var/log/nginx/tracking-error.log;

    location / {
        proxy_pass  http://tracking/webapp;
    }
}

在我当前的apache负载均衡器配置中,我有AllowEncodedSlashes指令保留了我的编码参数:

AllowEncodedSlashes NoDecode

我需要从apache转到nginx。

我的问题与这个问题完全相反:Avoid nginx escaping query parameters on proxy_pass

5 个答案:

答案 0 :(得分:38)

我终于找到了解决方案:我需要通过$request_uri parameter

location / {
    proxy_pass  http://tracking/webapp$request_uri;
}

这样,在原始请求中编码的字符将被解码,即将按原样传递给代理服务器。

答案 1 :(得分:13)

Jean's answer很好,但它不适用于sublocations。在这种情况下,更通用的答案是:

location /path/ {
  if ($request_uri ~* "/path/(.*)") {
    proxy_pass http://tracking/webapp/$1;
  }
}

答案 2 :(得分:10)

Nginx proxy_pass指令

有一个记录的选项
  

如果需要以未处理的形式传输URI,则应使用指令proxy_pass 不带URI部分

location  /some/path/ {
  proxy_pass   http://127.0.0.1;
}

所以在你的情况下它可能是这样的。不要担心它将被传递给上游服务器的请求URI

location / {
    proxy_pass  http://tracking;
}

希望它有所帮助。

答案 3 :(得分:5)

请注意,nginx文档中的URL解码,通常称为 $uri"规范化" ,发生在后端IFF之前:

  • 任何URI都在proxy_pass内指定,即使只是尾随斜杠,

  • 或者,URI在处理期间更改,例如通过rewrite

这两个条件都明确记录在http://nginx.org/r/proxy_pass(强调我的):

  •   

    如果{<1}}指令 指定了 ,那么当请求传递给服务器时, normalized 请求与该位置匹配的URI将被指令

    中指定的URI替换
  •   

    如果指定{strong> 没有URI proxy_pass,请求URI将以 相同的形式传递到服务器处理原始请求时,客户 完整的 规范化 请求当 处理 更改的URI

    时,会通过 传递URI

解决方案取决于您是否需要更改前端和后端之间的URL。

  • 如果不需要更改URI:

    proxy_pass
  • 否则,如果您确实需要在后端交换或映射前端的# map `/foo` to `/foo`: location /foo { proxy_pass http://localhost:8080; # no URI -- not even just a slash } /api,那么您可以从$request_uri获取原始URI变量,并使用类似于DFA的/app变量rewrite指令(BTW,如果您想要更多$uri DFA操作,请查看mdoc.su)。请注意,如果有人试图绕过您的第二个重写规则,则需要rewrite部分,因为它不会匹配return 400之类的内容。

    //api/
  • 如果您只是想为后端添加前缀,那么您可以立即使用# map `/api` to `/app`: location /foo { rewrite ^ $request_uri; # get original URI rewrite ^/api(/.*) /app$1 break; # drop /api, put /app return 400; # if the second rewrite won't match proxy_pass http://localhost:8080$uri; } 变量:

    $request_uri

您可能还想查看a related answer,其中显示了与上述类似的代码的一些测试运行。

答案 4 :(得分:-2)

在某些情况下,问题不在nginx端 - 您必须将Tomcat连接器上的uri编码设置为UTF-8。