这是我的 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/1234
到http://localhost:1234
,所以我只想提取端口号,将其从 url 中删除并将其用于 proxy_pass。
答案1
首先,由于您使用了字符串结尾锚点,因此您的^/(123[0-9])$
正则表达式将仅匹配/1234
URI(或/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;
}