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

Jea*_*ana 23 reverse-proxy load-balancing nginx

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

传入nginx的请求:

curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http%3A%2F%2Fwww.google.com%2F"
Run Code Online (Sandbox Code Playgroud)

传入tomcat的请求:

curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http:/www.google.com/"
Run Code Online (Sandbox Code Playgroud)

我不希望我的请求参数被转换,因为在那种情况下我的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

AllowEncodedSlashes NoDecode
Run Code Online (Sandbox Code Playgroud)

我需要从apache转移到nginx.

我的问题与这个问题完全相反:避免nginx在proxy_pass上转义查询参数

Jea*_*ana 44

我终于找到了解决方案:我需要传递$request_uri参数:

location / {
    proxy_pass  http://tracking/webapp$request_uri;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 哇,这确实有效,应该是默认的.我不明白为什么nginx需要干扰编码. (8认同)

use*_*062 13

Jean的答案很好,但它不适用于子位置.在这种情况下,更通用的答案是:

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


Cas*_*sey 10

Nginx proxy_pass指令有一个记录的选项

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

location  /some/path/ {
  proxy_pass   http://127.0.0.1;
}
Run Code Online (Sandbox Code Playgroud)

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

location / {
    proxy_pass  http://tracking;
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.

  • 谢谢!这对我有用.最新的jenkins(1.554.1)抱怨我的代理配置错误,因为编码斜线处理不当.我通过nginx配置更改为"location/jenkins {proxy_pass [http:// localhost:8080](http:// localhost:8080); ..}"并且jenkins错误消失了. (2认同)
  • 仅供参考,我遇到了这个问题,因为我在后端的末尾有一个`/`,例如`proxy_pass http:// my-backend /`。删除最终的`/`解决了问题(经过4小时的反复试验)。 (2认同)

cns*_*nst 6

请注意,URL解码(在Nginx文档中通常称为$uri“规范化”)发生在后端IFF之前:

  • 要么在proxy_pass其内部指定了任何URI ,即使仅是尾部的斜杠本身,

  • 或者,在处理过程中,例如,通过更改URI rewrite


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

  • 如果使用URIproxy_pass指定了伪指令,那么当请求传递到服务器时,与该位置匹配的规范化请求URI 的一部分将由伪指令中指定的URI替换。

  • 如果proxy_pass指定为不带URI,则将请求URI以与原始请求处理时客户端发送的格式相同的形式传递给服务器,或者处理更改的URI 传递完整的规范化请求URI。


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

  • 如果不需要更改URI:

    # map `/foo` to `/foo`:
    location /foo {
        proxy_pass  http://localhost:8080;  # no URI -- not even just a slash
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 否则,如果您确实需要在后端/api与前端交换或映射/app,则可以从$request_uri变量获取原始URI,然后对变量使用rewrite指令,$uri类似于DFA(BTW,如果需要更多rewriteDFA操作,请查看mdoc.su)。请注意,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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您只想为后端添加前缀,则可以立即使用$request_uri变量:

    # add `/webapp` to the backend:
    location / {
        proxy_pass    http://localhost:8080/webapp$request_uri;
    }
    
    Run Code Online (Sandbox Code Playgroud)

您可能还想看看一个相关的答案,该答案显示了一些类似于上述代码的测试运行。