Nginx

使用 URI 的一部分作為 nginx proxy_pass 指令的埠號

  • October 20, 2021

這是我的 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。

首先,您的^/(123[0-9])$正則表達式將僅匹配/1234URI(或/1230,/1231等)但不匹配,/1234/some/path因為您使用的$是字元串結尾錨點。我假設這不是錯誤,而是設計解決方案。如果不是,要同時匹配/1234and /1234/some/path(但不是類似的東西/12345),您可以使用交替:(^/(123[0-9])(?:/|$)我在(?:...)這裡使用的是非擷取組,因為它被認為具有比擷取組稍好的性能)。

你的配置有一大堆各種各樣的錯誤。讓我們來看看每一個。

錯誤#1:不正確rewrite ... last;的用法。

實際上,您不能proxy_pass像嘗試使用尾部斜杠那樣為上游指定 URI

proxy_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;breakrewrite ... 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;
}

引用自:https://serverfault.com/questions/1080986