使用 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/1234
到http://localhost:1234
,所以我只想提取埠號,將其從 url 中刪除並將其用於 proxy_pass。
首先,您的
^/(123[0-9])$
正則表達式將僅匹配/1234
URI(或/1230
,/1231
等)但不匹配,/1234/some/path
因為您使用的$
是字元串結尾錨點。我假設這不是錯誤,而是設計解決方案。如果不是,要同時匹配/1234
and/1234/some/path
(但不是類似的東西/12345
),您可以使用交替:(^/(123[0-9])(?:/|$)
我在(?:...)
這裡使用的是非擷取組,因為它被認為具有比擷取組稍好的性能)。你的配置有一大堆各種各樣的錯誤。讓我們來看看每一個。
錯誤#1:不正確
rewrite ... last;
的用法。實際上,您不能
proxy_pass
像嘗試使用尾部斜杠那樣為上游指定 URIproxy_pass http://localhost:$1/; # add slash not allow
內部正則表達式匹配位置(以及內部命名位置)並且更改將傳遞給後端的 URI 的唯一方法是使用該
rewrite
指令。但是,更改location
塊內的 URI 以便稍後在同一位置處理它的正確方法是使用rewrite ... break;
,因為rewrite ... last;
將強制 nginx 搜尋新位置以查找重寫的 URI。這意味著我們需要為指令使用break
標誌:rewrite
rewrite ^/[0-9]{4}(.*)$ $1 break;
**謹防!**不會在(或簡單地)指令(更新:經過一些測試後,與文件中所說的相反)
ngx_http_rewrite_module
執行任何指令,結果證明它僅適用於指令而不是指令,至少對於 OpenResty 1.17。 8.2 基於 nginx 17.8 核心)。rewrite ... break;``break;
break
rewrite ... break
錯誤 #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 地址指定時,您需要指定 a ,否則您將在 nginx 錯誤日誌中收到以下錯誤:沒有定義解析器來解析 localhost
通常你必須使用類似的東西
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; }