Skip to content

Commit 4b391f7

Browse files
committed
feat: 添加 proxy_pass 指令技巧
1 parent d088a0e commit 4b391f7

File tree

3 files changed

+173
-1
lines changed

3 files changed

+173
-1
lines changed

docs/.vuepress/config.js

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ function genSidebarConfigExample (title) {
9595
'expires',
9696
'autoindex',
9797
'add_header',
98+
'proxy_pass',
9899
]
99100
}
100101
]

docs/example/proxy_pass.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# proxy_pass url 反向代理的坑
2+
3+
说到 Nginx 就不得不说 Nginx 的反向代理是多么的好用,一个指令 `proxy_pass` 搞定反向代理,对于接口代理、负载均衡很是实用,但 `proxy_pass` 指令后面的参数很有讲究。
4+
5+
网上有很多什么绝对路径、相对路径的说法,其实在实际的应用中就分为两种情况:
6+
7+
## url 只是 host
8+
9+
这里指不包含 `$uri` ,如:
10+
11+
- `http://host` - √
12+
- `https://host` - √
13+
- `http://host:port` - √
14+
- `https://host:port` - √
15+
- `http://host/` - x
16+
- `http://host:port/` - x
17+
18+
这时候 `location` 匹配的完整路径将直接透传给 url ,如:
19+
20+
```nginx
21+
// 访问: / 后端: /
22+
// 访问: /api/xx 后端: /api/xx
23+
// 访问: /api/xx?aa 后端: /api/xx?aa
24+
location / {
25+
proxy_pass http://node:8080;
26+
}
27+
28+
// 访问: /api/ 后端: /api/
29+
// 访问: /api/xx 后端: /api/xx
30+
// 访问: /api/xx?aa 后端: /api/xx?aa
31+
// 访问: /api-xx?aa 后端:
32+
location /api/ {
33+
proxy_pass http://node:8080;
34+
}
35+
36+
// 访问: /api/ 后端: /api/
37+
// 访问: /api/xx 后端: /api/xx
38+
// 访问: /api/xx?aa 后端: /api/xx?aa
39+
// 访问: /api-xx?aa 后端: /api-xx?aa
40+
location /api {
41+
proxy_pass http://node:8080;
42+
}
43+
```
44+
45+
## url 包含路径
46+
47+
注意,这里的路径哪怕只是一个 `/` 也是存在的,如:
48+
49+
- `http://host` - x
50+
- `https//host/` - √
51+
- `http://host:port` - x
52+
- `https://host:port/` - √
53+
- `http://host/api` - √
54+
- `http://host/api/` - √
55+
56+
`proxy_pass url``url` 包含路径时,匹配时会根据 `location` 的匹配后的链接透传给 `url` ,注意匹配后就是这样:
57+
58+
| `location` 规则 | 访问的原始链接 | 匹配之后的路径 |
59+
| --- | --- | --- |
60+
| `location /` | `/` | ` ` |
61+
| `location /` | `/a` | `a` |
62+
| `location /` | `/a/b/c?d` | `a/b/c?d` |
63+
| `location /a/` | `/a/` | ` ` |
64+
| `location /a/` | `/a/b/c?d` | `b/c?d` |
65+
66+
明白匹配之后的路径后,在 `proxy_pass url` 包含路径时,将会把匹配之后的路径透传给 `url` ,如:
67+
68+
69+
```nginx
70+
// 访问: / 后端: /
71+
// 访问: /api/xx 后端: /api/xx
72+
// 访问: /api/xx?aa 后端: /api/xx?aa
73+
location / {
74+
proxy_pass http://node:8080/;
75+
}
76+
77+
// 访问: /api/ 后端: /
78+
// 访问: /api/xx 后端: /xx
79+
// 访问: /api/xx?aa 后端: /xx?aa
80+
// 访问: /api-xx?aa 后端:
81+
location /api/ {
82+
proxy_pass http://node:8080/;
83+
}
84+
85+
// 访问: /api 后端: /
86+
// 访问: /api/ 后端: //
87+
// 访问: /api/xx 后端: //xx
88+
// 访问: /api/xx?aa 后端: //xx?aa
89+
// 访问: /api-xx?aa 后端: /-xx?aa
90+
location /api {
91+
proxy_pass http://node:8080/;
92+
}
93+
94+
// 访问: /api/ 后端: /v1
95+
// 访问: /api/xx 后端: /v1xx
96+
// 访问: /api/xx?aa 后端: /v1xx
97+
// 访问: /api-xx?aa 后端: /-xx?aa
98+
location /api/ {
99+
proxy_pass http://node:8080/v1;
100+
}
101+
102+
// 访问: /api/ 后端: /v1/
103+
// 访问: /api/xx 后端: /v1/xx
104+
// 访问: /api/xx?aa 后端: /v1/xx
105+
// 访问: /api-xx?aa 后端:
106+
location /api/ {
107+
proxy_pass http://node:8080/v1/;
108+
}
109+
```
110+
111+
由以上规则可以看出,当 `proxy_pass url` 中包含路径时,结尾的 `/` 最好同 `location` 匹配规则一致。
112+
113+
## 当 proxy_pass 遇到正则
114+
115+
`location` 以正则形式匹配时,`proxy_pass` 就不能以 `/` 结束了,也就是不能包含路径了,比如错误的:
116+
117+
```nginx
118+
location ~* ^/api/ {
119+
proxy_pass http://host/;
120+
}
121+
122+
location / {
123+
if ($uri ~* ^/api/) {
124+
proxy_pass http://host/;
125+
}
126+
}
127+
```
128+
129+
解决办法就是把链接中的路径去掉。
130+
131+
## 重写代理链接 - url rewrite
132+
133+
当原始链接(浏览器访问的链接)和代理服务器链接规则不一致时,可以使用 Nginx URL Rewrite 功能去动态的重写,如:
134+
135+
```nginx
136+
location ~* ^/api/ {
137+
rewrite ^/api/(.*) /?path=$1 break;
138+
proxy_pass http://node:8080;
139+
}
140+
```
141+
142+
以上请求会把匹配 `/api/` 的链接重写为 `/?path=` 的链接透传给 `node:8080` 服务,有意思的是当使用 `rewrite` 指令并且生效后,`proxy_pass url` 链接中的路径会被忽略,如:
143+
144+
```nginx
145+
// 访问: / 后端: /node/
146+
// 访问: /api 后端: /node/api
147+
// 访问: /api/ 后端: /?path=
148+
// 访问: /api/a/b/c 后端: /?path=a/b/c
149+
location / {
150+
rewrite ^/api/(.*) /?path=$1 break;
151+
proxy_pass http://node:8080/node/;
152+
}
153+
```

docs/guide/error.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,22 @@ pid 进程 id 文件不存在,可能文件被删除或者已经停止,在停
3636

3737
### nginx: [emerg] "add_header" directive is not allowed here in xx
3838

39-
`add_header` 指令不能直接在 `if` 判断内,可以在 `http``server``server.location``server.location.if` 下。
39+
`add_header` 指令不能直接在 `if` 判断内,可以在 `http``server``server.location``server.location.if` 下。
40+
41+
### nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in xx
42+
43+
这是 `proxy_pass` 指令在正则匹配时不能使用包含路径的链接,如以下都会报错:
44+
45+
```nginx
46+
location ~* ^/api/ {
47+
proxy_pass http://host/;
48+
}
49+
50+
location / {
51+
if ($uri ~* ^/api/) {
52+
proxy_pass http://host/;
53+
}
54+
}
55+
```
56+
57+
解决办法就是把 `/` 去掉,更多关于 `proxy_pass` 请看:[`proxy_pass` 技巧](/example/proxy_pass.html)

0 commit comments

Comments
 (0)