|
| 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 | +``` |
0 commit comments