2015-02-23 33 views
16

Nginx的pass_proxy子目录我需要写一个nginx的位置指令请求代理到子目录到另一台服务器保存URL编码删除子目录前缀没有URL解码

这里是一个人为的例子 - 要求是这样的:

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

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

  • another SO question
  • 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的bug跟踪系统

    +0

    你可以试试lua。但首先你不需要这个,根据HTTP规格这些网址是相同的 – 2015-02-24 04:48:08

    +0

    或者使用子域而不是子目录 – 2015-02-24 04:49:37

    +0

    @AlexeyTen在http:// abcd.com上运行的服务器以不同的方式处理这些请求,我没有控制它。你知道该http规范摘录的链接吗?我找不到它 – 2015-02-24 08:49:32

    回答

    23

    一个ticket但有没有简单的方法来解决这个nginx的行为。 nginx trac中有一些bug,你可以添加你的。 trac.nginx.org/nginx/...。所以,我认为最简单的方法是有子域。 - 阿列克谢十二月在24 '15 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

    认真..谢谢你。真正的时刻,它实际上按预期工作。鉴于与现有规则的相似性,魔术显然在$ request_uri捕获中。 – Joe 2016-09-27 15:48:42

    +0

    为了避免'重写的URI有零长度'的错误,我用 重写^ $ request_uri; 重写^/api(/.*)$ 1 break; return 400; proxy_pass http://127.0.0.1:82$uri; – 2018-02-08 15:17:13