你的疑问?
vite
proxy: {
'/api/': {
target: baseUrl,
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, '')
}
}
nginx
location /api/ {
加上这个斜杠,效果等于 Vite 的 rewrite
proxy_pass http://你的Java服务器内部IP:8080/;
标准配置:把真实的客户端信息传给 Java
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
vite的代理,既然最后都要把它删掉,为什么还要费劲巴拉地先加上去,然后再用正则删掉?这看起来简直像是在玩"脱了袜子放屁"的游戏。
其实,这并不是多此一举,而是一个非常精妙的路由分发信号。
为了让你彻底理解,我给你打个比方。
1. 核心原因:区分"文件"和"接口"
假设你的前端项目(Vite)是一个巨大的公司。
-
静态资源部 :负责处理
.js,.css,.png,index.html(这些是真实存在你硬盘里的文件)。 -
外联部(Proxy) :负责把请求发给后端大厨(Java 接口)。
如果你直接请求 localhost:5173/getsingelUser/1,Vite 会怎么想? Vite 会去你的硬盘里找:"咦?我的文件夹里有一个叫 getsingelUser/1 的文件吗?" 找了一圈没找到,它就会报 404。
所以,我们需要一个"标志"。
当你请求 /api/getsingelUser/1 时:
-
Vite 一看 :哦!路径开头是
/api。 -
触发判断 :它知道这不是找本地文件的,而是要交给"外联部"转发的。
-
开始转发 :外联部接手后,发现后端大厨其实不认识
/api这个暗号。 -
执行 Rewrite :为了不让大厨懵逼,外联部贴心地把
/api抹掉,变成大厨认识的/getsingelUser/1发过去。
2. 如果不配置 /api 会发生什么?
如果你想尝试"省掉"这一步,直接把 proxy 配置成匹配所有路径(比如 /):
JavaScript
proxy: {
'/': { // 匹配所有路径
target: baseUrl,
changeOrigin: true
}
}
灾难发生了: 当你刷新页面想看网页时,浏览器请求 index.html。
-
Vite 拦截到
/index.html。 -
发现符合
/的匹配规则。 -
强行转发:它把请求发给了 Java 后端。
-
后端报错 :Java 大厨一看,"我要的是 ID,你给我个
index.html干啥?"
结果就是:你的网页打不开了,因为所有的静态文件请求都被当成接口转发走了。
3. 深度分析:为什么要 Rewrite 而不直接让后端支持 /api?
你可能会问:"那我不重写,我让 Java 接口也叫 /api/getsingelUser 不就行了?"
当然可以!如果你这样做,配置就变成了:
JavaScript
proxy: {
'/api': {
target: baseUrl,
changeOrigin: true
// 不需要 rewrite 了!
}
}
这种做法在很多公司也非常流行。 但为什么要 rewrite 的方案更常见呢?
-
后端独立性 :后端接口可能已经写好了(比如就是
/test),为了配合前端的跨域需求去改所有后端的代码,代价太大。 -
环境统一 :有时候你本地开发需要
/api做区分,但上线后的真实域名(比如api.zhurenjin.com)本身就代表了接口,不需要路径前缀。通过rewrite,你可以让前端代码保持逻辑一致。
nginx
既然你理解了 Vite 的"红袖标"原理,那 Nginx 的 location /api/ 就非常好懂了。它在生产环境(线上服务器)扮演的角色,和 Vite 在开发环境扮演的角色一模一样。
但是,Nginx 的语法有一个非常微妙的"陷阱",决定了它是否会自动帮你把 /api 删掉。
1. Nginx 转发的两种写法(最核心的区别)
假设你前端请求的地址是:http://你的服务器IP/api/getsingelUser/1
方案 A:自动删掉 /api(对应 Vite 的 rewrite)
如果你希望转发给 Java 的路径是 /getsingelUser/1,你需要在 proxy_pass 的 IP 地址后面加一个斜杠 /。
Nginx
location /api/ {
# 注意:IP 端口后面有斜杠!
proxy_pass http://120.xx.xx.xx:8080/;
}
-
逻辑 :Nginx 发现你请求里匹配了
/api/。因为它看到proxy_pass后面带了路径(哪怕只是一个/),它就会把请求中匹配location的那部分(/api/)替换 成proxy_pass后面的路径(/)。 -
结果 :
/api/getsingelUser/1\\rightarrow[http://120.xx.xx.xx:8080/getsingelUser/1](http://120.xx.xx.xx:8080/getsingelUser/1)。
方案 B:保留 /api(原封不动转发)
如果你希望转发给 Java 的路径依然带着 /api,那么 proxy_pass 的 IP 后面不要加任何东西。
Nginx
location /api/ {
# 注意:IP 端口后面没有斜杠!
proxy_pass http://120.xx.xx.xx:8080;
}
-
逻辑:Nginx 认为这只是一个纯粹的代理,它会把整个原始路径直接拼在后面。
-
结果 :
/api/getsingelUser/1\\rightarrow[http://120.xx.xx.xx:8080/api/getsingelUser/1](http://120.xx.xx.xx:8080/api/getsingelUser/1)。
2. 为什么我们要配置 Nginx 代理线上 IP?
当你的前端页面(HTML/JS)部署在 Nginx 上时,浏览器认为这个页面的"家"在 http://你的服务器IP。
-
避开跨域限制 :浏览器允许网页请求自己"家"里的资源。所以你请求
/api/xxx是合法的(同源)。 -
隐藏真实端口 :你的 Java 可能开在
8080或9000。如果你直接让前端访问这些端口,你得在云服务器后台开一堆防火墙端口,很不安全。 -
统一入口 :通过 Nginx,你只需要对外开放一个
80端口。所有的前端网页、后端接口、甚至图片资源,全部通过这一个大门进出。
3. 最佳实践:我该怎么配置?
既然你本地 Vite 配置了 rewrite,说明你的 Java 代码里 @GetMapping 是不带 /api 的。为了保持一致,线上 Nginx 建议这样配置:
Nginx
server {
listen 80;
server_name 你的域名或公网IP;
# 1. 前端静态页面
location / {
root /usr/share/nginx/html/dist; # 你前端打包放的位置
index index.html;
try_files $uri $uri/ /index.html;
}
# 2. 后端接口代理
location /api/ {
# 加上这个斜杠,效果等于 Vite 的 rewrite
proxy_pass http://你的Java服务器内部IP:8080/;
# 标准配置:把真实的客户端信息传给 Java
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
💡 总结下:
-
Vite 代理 :靠
rewrite正则表达式手动删掉/api。 -
Nginx 代理 :靠
proxy_pass结尾的那个斜杠/自动替换掉/api。
一句话: 只要你发现线上报 404,先去检查 Nginx 配置里 proxy_pass 那个 IP 后面有没有漏掉斜杠!