proxy_pass url 反向代理的坑
說到 Nginx 就不得不說 Nginx 的反向代理是多么的好用,一個指令 proxy_pass
搞定反向代理,對於接口代理、負載均衡很是實用,但 proxy_pass
指令后面的參數很有講究。
網上有很多什么絕對路徑、相對路徑的說法,其實在實際的應用中就分為兩種情況:
url 只是 host
這里指不包含 $uri
,如:
http://host
- √https://host
- √http://host:port
- √https://host:port
- √http://host/
- xhttp://host:port/
- x
這時候 location
匹配的完整路徑將直接透傳給 url ,如:
// 訪問: / 后端: /
// 訪問: /api/xx 后端: /api/xx
// 訪問: /api/xx?aa 后端: /api/xx?aa
location / {
proxy_pass http://node:8080;
}
// 訪問: /api/ 后端: /api/
// 訪問: /api/xx 后端: /api/xx
// 訪問: /api/xx?aa 后端: /api/xx?aa
// 訪問: /api-xx?aa 后端:
location /api/ {
proxy_pass http://node:8080;
}
// 訪問: /api/ 后端: /api/
// 訪問: /api/xx 后端: /api/xx
// 訪問: /api/xx?aa 后端: /api/xx?aa
// 訪問: /api-xx?aa 后端: /api-xx?aa
location /api {
proxy_pass http://node:8080;
}
url 包含路徑
注意,這里的路徑哪怕只是一個 /
也是存在的,如:
http://host
- xhttps//host/
- √http://host:port
- xhttps://host:port/
- √http://host/api
- √http://host/api/
- √
當 proxy_pass url
的 url
包含路徑時,匹配時會根據 location
的匹配后的鏈接透傳給 url
,注意匹配后就是這樣:
location 規則 |
訪問的原始鏈接 | 匹配之后的路徑 |
---|---|---|
location / |
/ |
|
location / |
/a |
a |
location / |
/a/b/c?d |
a/b/c?d |
location /a/ |
/a/ |
|
location /a/ |
/a/b/c?d |
b/c?d |
明白匹配之后的路徑后,在 proxy_pass url
包含路徑時,將會把匹配之后的路徑透傳給 url
,如:
// 訪問: / 后端: /
// 訪問: /api/xx 后端: /api/xx
// 訪問: /api/xx?aa 后端: /api/xx?aa
location / {
proxy_pass http://node:8080/;
}
// 訪問: /api/ 后端: /
// 訪問: /api/xx 后端: /xx
// 訪問: /api/xx?aa 后端: /xx?aa
// 訪問: /api-xx?aa 未匹配
location /api/ {
proxy_pass http://node:8080/;
}
// 訪問: /api 后端: /
// 訪問: /api/ 后端: //
// 訪問: /api/xx 后端: //xx
// 訪問: /api/xx?aa 后端: //xx?aa
// 訪問: /api-xx?aa 后端: /-xx?aa
location /api {
proxy_pass http://node:8080/;
}
// 訪問: /api/ 后端: /v1
// 訪問: /api/xx 后端: /v1xx
// 訪問: /api/xx?aa 后端: /v1xx
// 訪問: /api-xx?aa 未匹配
location /api/ {
proxy_pass http://node:8080/v1;
}
// 訪問: /api/ 后端: /v1/
// 訪問: /api/xx 后端: /v1/xx
// 訪問: /api/xx?aa 后端: /v1/xx
// 訪問: /api-xx?aa 未匹配
location /api/ {
proxy_pass http://node:8080/v1/;
}
由以上規則可以看出,當 proxy_pass url
中包含路徑時,結尾的 /
最好同 location
匹配規則一致。
當 proxy_pass 遇到正則
當 location
以正則形式匹配時,proxy_pass
就不能以 /
結束了,也就是不能包含路徑了,比如錯誤的:
location ~* ^/api/ {
proxy_pass http://host/;
}
location / {
if ($uri ~* ^/api/) {
proxy_pass http://host/;
}
}
解決辦法就是把鏈接中的路徑去掉。
重寫代理鏈接 - url rewrite
當原始鏈接(瀏覽器訪問的鏈接)和代理服務器鏈接規則不一致時,可以使用 Nginx URL Rewrite 功能去動態的重寫,如:
location ~* ^/api/ {
rewrite ^/api/(.*) /?path=$1 break;
proxy_pass http://node:8080;
}
以上請求會把匹配 /api/
的鏈接重寫為 /?path=
的鏈接透傳給 node:8080
服務,有意思的是當使用 rewrite
指令並且生效后,proxy_pass url
鏈接中的路徑會被忽略,如:
// 訪問: / 后端: /node/
// 訪問: /api 后端: /node/api
// 訪問: /api/ 后端: /?path=
// 訪問: /api/a/b/c 后端: /?path=a/b/c
location / {
rewrite ^/api/(.*) /?path=$1 break;
proxy_pass http://node:8080/node/;
}