没有url解码的Nginx pass_proxy子目录

rin*_*.io 26 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. 这个SO问题

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. 来自另一个SO问题

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跟踪器中的一张

cns*_*nst 42

但是没有简单的方法来修复这个nginx行为.nginx trac中有一些bug,你可以添加你的bug.trac.nginx.org/nginx/....所以,我认为最简单的方法是拥有子域名. - Alexey十二月二十四日二十四日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;
    }
Run Code Online (Sandbox Code Playgroud)

伙计就是这样!


这是完整的证据.

配置文件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;
}
Run Code Online (Sandbox Code Playgroud)

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

% 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
%
Run Code Online (Sandbox Code Playgroud)

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


PS如果您认为使用重写引擎作为有限状态自动机非常酷,您可能还需要查看我的http://mdoc.su/项目,或者将它转换为github.

  • 说真的……谢谢你。当它真正按预期工作时的真正哇时刻。鉴于与现有规则的相似性,魔术显然在于 $request_uri 捕获。 (3认同)
  • 我想知道后来的 nginx 版本是否发生了变化。尽我所能,使用 nginx 1.13.12 和这种方法,原始 request_uri 中存在的任何 URL 编码(例如 %2F)而不是再次编码(因此变为 %252F) (3认同)
  • 为了避免“重写的 URI 长度为零”错误,我使用了 rewrite ^ $request_uri; 重写 ^/api(/.*) $1 中断;返回 400;proxy_pass http://127.0.0.1:82$uri; (2认同)