彻底理解 Nginx 中 location 和 proxy_pass 的交互机制,是后端开发和运维的一项核心技能。它能帮助避免未来 90% 的代理配置问题。
1. 核心概念:Ngxin 就像一个聪明的转发台
想象一下,整套系统是一栋大楼:
- 前端浏览器:大楼的访客。
- nginx(端口 8080):大楼的总前台,所有访客必须先经过它。
- 后端 springboot(8081):真正的业务部门,在办公室内。
proxy_pass
指令,就是前台接待员(Nginx)手册上的一条规定,告诉她如何把访客的请求转交给业务部门。
这条规定是最关键、最让人困惑的地方,就是地址(URL)末尾有没有斜杠 /。
2. 场景 1:porxy_pass 地址【没有】斜杠 /
bash
location /api {
proxy_pass http://backend:8080;
}
解读:
location /api
:当前台听到访客说要去以/api
开头时,就应用这条规则。proxy_pass http://backend:8080
:把访客的完整请求路径 直接拼接到http://backend:8080
。
实际流程:
- 访客 (axios) 说:"你好,我要去
/api/user/register
"。 - 前台 (Nginx) 一听,开头是
/api
,符合手册规则。 - 她拿出内部电话,拨通了
http://backend:8080
。 - 然后她对电话说:"你好,有个访客要去
/api/user/register
"。 - 最终,业务部门 (Spring Boot) 收到的请求就是
/api/user/register
。
这个方案的问题:它把路由前缀 /api 泄露给了后端业务部门。这是一种"耦合",后端被迫要知道一些它本不该关心的"前台路由规则"。如果以后前台把 /api 改成了 /v2/api,后端所有代码都得跟着改,非常麻烦。
3. 场景 2:porxy_pass 地址【有】斜杠 /
这相当于前台手册上写着:"把访客请求中我们约定好的'暗号'去掉,只把真正要去的地方告诉业务部门。"
bash
location /api/ { # 为了精确,这里也推荐加上斜杠
proxy_pass http://backend:8080/;
}
解读:
location /api/
:当前台听到访客说要去的地方以/api/
开头时(这里的/
很重要),应用这条规则。这个/api/
就是我们约定的"暗号"。proxy_pass http://backend:8080/
:关键来了! 这个末尾的 / 告诉前台:"把访客请求中匹配到 location 的那部分(也就是暗号/api/
)扔掉,只把剩下的部分拼接到http://backend:8080/
后面。 "
实际流程:
- 访客 (axios) 说:"你好,我要去
/api/user/register
"。 - 前台 (Nginx) 一听,开头是
/api/
,符合手册规则。 - 她心里默念:"好的,暗号是
/api/
,真正要去的地方是/user/register
。" - 她拿出内部电话,拨通了
http://backend:8080/
。 - 然后她对电话说:"你好,有个访客要去
/user/register
"。 - 最终,业务部门 (Spring Boot) 收到的请求就是
/user/register
。
这个方案的优点 (行业标准) :
- 解耦 (Decoupling) :后端业务部门完全不知道 /api 这个前缀的存在,它只关心自己的业务路径,比如 /user/register。
- 灵活性 :未来如果因为业务发展,需要把 URL 从
http://your.domain/api/...
改成http://your.domain/v2/api/...
,只需要修改 Nginx 的 location 配置,后端的所有 Java 代码一行都不用动!
前端配置:
javascript
import { defineConfig } from 'vite';
// ...其他 import
export default defineConfig({
// ...其他配置
server: {
proxy: {
// 当请求路径以 /api 开头时,应用此规则
'/api': {
// 代理到你本地 Spring Boot 服务的地址
target: 'http://localhost:8080',
// 必须开启,服务器才能识别正确的 Host 头
changeOrigin: true,
// 【关键】重写路径,去掉 /api 前缀
rewrite: (path) => path.replace(/^/api/, ''),
},
},
},
});
csharp
import axios from 'axios';
// 注册请求
function registerUser(userData) {
// 请求路径必须以 /api 开头
return axios.post('/api/user/register', userData);
}
// 获取文章列表
function getArticleList(params) {
// 请求路径必须以 /api 开头
return axios.get('/api/article/list', { params });
}
- 收到
/api/user/register
-> 转发/user/register
。 - 最终收到请求:
http://your.domain/user/register
。
4. 总结
proxy_pass
不带 / -> "忠实传递" -> 拼接完整 URI。proxy_pass
带 / -> "剥离暗号" -> 只拼接 location 匹配之外的剩余部分。