没有url解码的Nginx pass_proxy子目录

时间:2015-02-23 21:59:09

标签: nginx

我需要编写一个nginx位置指令来将请求子目录代理到另一个服务器保留urlencoding 删除子目录前缀

这是一个人为的例子 - 请求如下:

http://1.2.3.4/api/save/http%3A%2F%2Fexample.com

应该以

传递

http://abcd.com/save/http%3A%2F%2Fexample.com

我尝试了几种不同的方法。这里有几个:

  1. 来自this SO question
  2. location /api/ { rewrite ^/api(/.*) $1 break; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_pass http://abcd.com; } 但它会对字符串进行解码,因此http://abcd.com会获得/save/http://example.com

    1. 来自another SO question
    2. location /api/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_pass http://abcd.com; }

      但它保留子目录,因此http://abcd.com获取/api/save/http%3A%2F%2Fexample.com

      需要的是中间的某个地方。谢谢!

      UPD:此处是nginx错误跟踪器中的ticket

2 个答案:

答案 0 :(得分:38)

  
    
      

但是没有简单的方法来修复这个nginx行为。 nginx trac中有一些bug,你可以添加你的bug。 trac.nginx.org/nginx/...。所以,我认为最简单的方法是拥有子域名。 - 阿列克谢十月二十四日二十四日14:49

    
         

https://trac.nginx.org/nginx/ticket/727

  
     

如果你想让nginx做一些自定义的事情,你可以使用带变量的proxy_pass(以及$ request_uri变量,它包含客户端发送的原始未转义请求URI)。在这种情况下,您有责任进行正确的URI转换。请注意,这很容易导致安全问题,应谨慎处理。

接受挑战!

    location /api/ {
        rewrite ^ $request_uri;
        rewrite ^/api/(.*) $1 break;
        return 400;
        proxy_pass http://127.0.0.1:82/$uri;
    }

就是这样,伙计们!

这是完整的证据。

nginx/1.2.1的配置文件:

server {
    listen 81;
    #first, the solution
    location /api/ {
        rewrite ^ $request_uri;
        rewrite ^/api/(.*) $1 break;
        return 400; #if the second rewrite won't match
        proxy_pass http://127.0.0.1:82/$uri;
    }
    #next, a few control groups
    location /dec/ {
        proxy_pass http://127.0.0.1:82/;
    }
    location /mec/ {
        rewrite ^/mec(/.*) $1 break;
        proxy_pass http://127.0.0.1:82;
    }
    location /nod/ {
        proxy_pass http://127.0.0.1:82;
    }
}

server {
    listen 82;
    return 200 $request_uri\n;
}

以下是为每个位置运行查询的结果:

% echo localhost:81/{api,dec,mec,nod}/save/http%3A%2F%2Fexample.com | xargs -n1 curl
/save/http%3A%2F%2Fexample.com
/save/http:/example.com
/save/http:/example.com
/nod/save/http%3A%2F%2Fexample.com
%

请注意,拥有额外的return 400;非常重要 - 否则,您可能会遇到安全问题(通过//api进行文件访问等),正如Maxim在您的trac票证中简要提到的那样。

P.S。如果您认为将重写引擎用作有限状态自动机非常酷,您可能还需要查看我的http://mdoc.su/项目或fork it github

答案 1 :(得分:0)

只要我们谈论的前缀是^~或没有修饰符,您的工作就很容易

location /api/ {
  # if you don't want to pass /api/ add a trailing slash to the proxy_pass
  proxy_pass http://localhost:8080/;

  ...
}

所有内容都将不经过解码而传递,您不必传递$uri

此外,在使用代理传递时,还应该设置这些标头

# pass headers and body along
proxy_pass_request_headers on;
proxy_pass_request_body on;

# set some headers to make sure the reverse proxy is passing along everything necessary
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;