使用 URI 的一部分作为 nginx proxy_pass 指令的端口号

使用 URI 的一部分作为 nginx proxy_pass 指令的端口号

这是我的 nginx 配置:

server {
        listen 443 ssl;
        server_name sub.example.fr ;
        location ~ ^/ (123[0-9])$ { # regex work
                #rewrite ^/[0-9]{4}(.*)$ $1 last; # do not work, with last or break
                #proxy_pass http://localhost:$1/; # add slash not allow
                proxy_pass http://localhost:$1;
        }
}

我想转发https://sub.example.fr/1234http://localhost:1234,所以我只想提取端口号,将其从 url 中删除并将其用于 proxy_pass。

答案1

首先,由于您使用了字符串结尾锚点,因此您的^/(123[0-9])$正则表达式将仅匹配/1234URI(或/1230/1231等),而不会匹配 。我假设这不是错误,而是一种设计解决方案。如果不是,要匹配和(但不是 之类的东西),您可以使用交替:(我在这里使用非捕获组,因为它被认为比捕获组具有略好的性能)。/1234/some/path$/1234/1234/some/path/12345^/(123[0-9])(?:/|$)(?:...)

您的配置有很多错误。让我们逐一检查一下。

错误#1:rewrite ... last;使用不正确。

确实,您无法proxy_pass像使用尾部斜杠那样指定上游的 URI

proxy_pass http://localhost:$1/; # add slash not allow

在正则表达式匹配位置内(以及在命名位置内),更改将传递给后端的 URI 的唯一方法是使用指令rewrite。但是,更改块内 URIlocation以便稍后在同一位置内处理它的正确方法是使用rewrite ... break;sincerewrite ... last;将强制 nginx 在新位置搜索重写的 URI。这意味着我们需要使用指令break的标志rewrite

rewrite ^/[0-9]{4}(.*)$ $1 break;

谨防!没有来自ngx_http_rewrite_module将在指令之后执行rewrite ... break;(或简称为break;指令(更新:经过一些测试,与文档,事实证明它只适用于break指令但不是rewrite ... break唯一的指令,至少对于基于 nginx 17.8 核心的 OpenResty 1.17.8.2 来说不是)。

错误 #2:您应该将任何包含花括号的字符串放在引号中,否则它们将被视为 nginx 配置块。

上述指令将导致以下错误:

nginx:[emerg] 指令“rewrite”未以“;”终止

为了消除此错误,我们需要用大括号括起我们的正则表达式模式:

rewrite "^/[0-9]{4}(.*)$" $1 break;

错误 #3:每当评估正则表达式时,编号的捕获都会被覆盖。

假设您有一个 URI /1234

location ~ ^/(123[0-9])$ {
    # Here the value of '$1' variable is "1234"
    rewrite "^/[0-9]{4}(.*)$" $1 break;
    # Here the value of '$1' variable is an empty string!
    proxy_pass http://localhost:$1; # There will be no port for 'proxy_pass' directive
}

您可以$1在重写规则评估其自己的正则表达式之前保存该值set指示:

location ~ ^/(123[0-9])$ {
    set $port $1;
    rewrite "^/[0-9]{4}(.*)$" $1 break;
    proxy_pass http://localhost:$port;
}

或者更好地利用命名捕获组

location ~ ^/(?<port>123[0-9])$ {
    rewrite "^/[0-9]{4}(.*)$" $1 break;
    proxy_pass http://localhost:$port;
}

有时,除了使用命名捕获之外,没有其他解决方案,这种情况的一个很好的例子是线。

错误 #4:重写的 URI 不能为空字符串。

再次假设你有一个 URI /1234。重写规则之后,内部 nginx$uri变量将具有空值。这将导致 HTTP 500 内部服务器错误,并且在 nginx 错误日志中您将看到以下错误消息:

重写的 URI 长度为零

要解决该错误,只需将任何 URI 重写为/

location ~ ^/(?<port>123[0-9])$ {
    rewrite ^ / break;
    proxy_pass http://localhost:$port;
}

如果您需要满足/<port_number>/some/path上述任何请求,则应使用以下位置块来确保重写的 URI 以斜杠开头:

location ~ ^/(?<port>123[0-9])(?:/|$) {
    rewrite "^/\d{4}(?:/(.*))?" /$1 break;
    proxy_pass http://localhost:$port;
}

错误 #5:在指令中使用变量时,需要指定解析器proxy_pass

上述配置接近可行的解决方案。但是它仍然不可行,会给出 HTTP 502 Bad Gateway 错误。您需要指定resolver当您在指令中使用变量proxy_pass并且您的上游由域名而不是 IP 地址指定时,否则您将在 nginx 错误日志中收到以下错误:

没有定义解析器来解析本地主机

一般来说你必须使用类似

resolver 8.8.8.8;
location ~ ^/(?<port>123[0-9])$ {
    rewrite ^ / break;
    proxy_pass http://localhost:$port;
}

但由于你的上游是localhost,你可以指定它的 IP 地址而不是域名,这样你就resolver根本不需要该指令:

location ~ ^/(?<port>123[0-9])$ {
    rewrite ^ / break;
    proxy_pass http://127.0.0.1:$port;
}

此配置应该完全可行(最终)。再说一次,如果您需要处理任何/<port_number>/some/path类似上述的请求,则应使用以下位置块:

location ~ ^/(?<port>123[0-9])(?:/|$) {
    rewrite "^/\d{4}(?:/(.*))?" /$1 break;
    proxy_pass http://127.0.0.1:$port;
}

相关内容