在部署芋道项目时,很多人一开始都会直接让前端去请求后端地址,比如:
text
http://后端域名/admin-api
或者打包后前端里还残留着:
text
http://localhost:48082
这样会带来几个常见问题:
- 后端地址直接暴露给用户
- 容易出现跨域问题
- 前端打包后接口还是请求 localhost
- 页面刷新后 404
- 更换后端地址时,还要重新改前端代码
更推荐的做法是:
- 前端页面访问自己的域名
- 前端请求接口时,也走自己的域名
- 由 Nginx 再把接口请求转发到真正的后端服务
这篇文章把这个问题完整讲清楚,包括:
- 为什么不能直接写
localhost - 为什么推荐前端走同域名请求
- 芋道管理后台前端配置怎么写
- 门户网站前端配置怎么写
- Nginx 怎么转发
- 页面刷新 404 怎么解决
- 常见报错怎么排查
一、问题现象
很多人部署完前端后,打开页面会发现:
- 登录验证码请求失败
- 接口请求地址还是
localhost - 浏览器控制台报错
- 页面一刷新就 404
- 明明后端在服务器上运行,前端却请求不到
例如浏览器里出现这样的请求地址:
text
http://localhost:48082/admin-api/system/captcha/get
这就是典型问题。
这里一定要明白:
前端页面是在用户浏览器里运行的。
当前端去请求 localhost:48082 时,请求的不是你的服务器,而是用户自己电脑的 localhost。
所以用户当然访问不到你服务器上的后端。
二、正确思路:前端请求自己的域名,Nginx 再转发到后端
正确做法不是让前端直接写后端地址,而是这样:
假设:
- 管理后台前端域名:
admin.example.com - 门户网站前端域名:
www.example.com - 后端服务运行在服务器本机:
127.0.0.1:48082
那么前端应该这样请求:
管理后台
前端请求:
text
https://admin.example.com/admin-api/...
Nginx 转发到:
text
http://127.0.0.1:48082/admin-api/...
门户网站
前端请求:
text
https://www.example.com/api/...
或者有些项目会写成:
text
https://www.example.com/app-api/...
Nginx 转发到:
text
http://127.0.0.1:48082/api/...
或者:
text
http://127.0.0.1:48082/app-api/...
三、为什么这样做更好
1. 后端地址不用暴露
用户只看到前端自己的域名,看不到真实后端地址和端口。
2. 避免跨域
因为前端和接口都走同一个域名或同源路径,浏览器不会拦截。
3. 更方便管理
以后后端换端口、换机器、换部署方式,只需要改 Nginx,不需要重新改前端代码。
4. 更安全
后端服务甚至可以只监听 127.0.0.1,完全不对公网开放。
四、前端配置文件怎么写
这一部分是很多新手最容易迷糊的地方。
你要记住一个核心原则:
前端不要直接写真实后端地址,而是写接口前缀。
也就是不要写:
js
baseURL: 'http://127.0.0.1:48082'
或者:
js
baseURL: 'http://后端域名'
而应该写成:
js
baseURL: '/admin-api'
或者:
js
baseURL: '/api'
这样前端发请求时,请的就是当前站点自己的域名,再由 Nginx 去转发。
五、门户网站前端配置文件示例
你给出的门户配置文件是这样的:
js
// 配置文件
export const config = {
// 开发环境用完整地址,生产环境通过 nginx 转发
baseURL: '/api',
default_headers: 'application/json'
};
这个写法其实就是比较标准的生产环境写法。
它的意思是:
- 前端代码里请求接口时,统一走
/api - 浏览器实际访问时,会自动拼成当前域名下的
/api - Nginx 再把
/api转发到真正的后端服务
例如:
前端代码里请求:
js
config.baseURL + '/user/info'
最终就是:
text
/api/user/info
浏览器实际发出的请求是:
text
https://www.example.com/api/user/info
然后 Nginx 转发到:
text
http://127.0.0.1:48082/api/user/info
门户网站推荐写法
js
// 配置文件
export const config = {
// 生产环境通过 nginx 转发到后端
baseURL: '/api',
default_headers: 'application/json'
};
如果你的门户项目开发环境和生产环境要区分
也可以写成这种方式,更容易理解:
js
const isDev = process.env.NODE_ENV === 'development';
export const config = {
baseURL: isDev ? 'http://127.0.0.1:48082/api' : '/api',
default_headers: 'application/json'
};
这样含义就很清楚:
- 本地开发时,前端直接请求本地后端
- 生产环境部署后,前端只请求
/api - 由 Nginx 去转发
六、芋道管理后台前端配置文件示例
芋道管理后台通常是 Vite 项目,很多时候会在 .env 文件里配接口地址。
比如原来有人会写成:
env
VITE_BASE_URL='http://localhost:48082'
VITE_API_URL=/admin-api
这个就有问题。
因为它会导致前端最终请求:
text
http://localhost:48082/admin-api/...
浏览器里这个 localhost 指向的是用户自己电脑,不是服务器。
芋道管理后台推荐写法
env
VITE_BASE_URL=''
VITE_API_URL=/admin-api
这样最终拼接出来的地址就是:
text
/admin-api/...
浏览器实际访问时会变成:
text
https://admin.example.com/admin-api/system/captcha/get
然后由 Nginx 转发到:
text
http://127.0.0.1:48082/admin-api/system/captcha/get
芋道后台前端配置的理解方式
如果你的请求封装代码里是这样拼接的:
js
baseURL = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL
那么:
env
VITE_BASE_URL=''
VITE_API_URL=/admin-api
最终结果就是:
js
'' + '/admin-api' = '/admin-api'
这正是我们想要的效果。
芋道后台环境变量完整示例
env
# 开发环境:本地只启动前端项目,依赖开发环境(后端、APP)
NODE_ENV=production
VITE_DEV=true
# 请求路径
VITE_BASE_URL=''
# 文件上传类型:server - 后端上传,client - 前端直连上传,仅支持S3服务
VITE_UPLOAD_TYPE=server
# 接口地址
VITE_API_URL=/admin-api
# 是否删除debugger
VITE_DROP_DEBUGGER=false
# 是否删除console.log
VITE_DROP_CONSOLE=false
# 是否sourcemap
VITE_SOURCEMAP=true
# 打包路径
VITE_BASE_PATH=/
# 输出路径
VITE_OUT_DIR=dist
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN=''
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
# GoView域名
VITE_GOVIEW_URL=''
这里最关键的只有两个:
env
VITE_BASE_URL=''
VITE_API_URL=/admin-api
七、管理后台 Nginx 配置示例
假设:
- 管理后台域名:
admin.example.com - 前端静态文件目录:
/www/wwwroot/admin-web/dist - 后端服务:
127.0.0.1:48082
nginx
server {
listen 80;
server_name admin.example.com;
root /www/wwwroot/admin-web/dist;
index index.html index.htm;
# 管理后台接口转发
location /admin-api/ {
proxy_pass http://127.0.0.1:48082/admin-api/;
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;
}
# 如果项目有 websocket,可加这个
location /infra/ws/ {
proxy_pass http://127.0.0.1:48082/infra/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
# 前端单页应用刷新防 404
location / {
try_files $uri $uri/ /index.html;
}
}
八、门户网站 Nginx 配置示例
如果你的门户配置文件里写的是:
js
baseURL: '/api'
那 Nginx 就应该把 /api 转发到后端。
假设:
- 门户网站域名:
www.example.com - 前端静态文件目录:
/www/wwwroot/portal-web/dist - 后端服务:
127.0.0.1:48082
nginx
server {
listen 80;
server_name www.example.com;
root /www/wwwroot/portal-web/dist;
index index.html index.htm;
# 门户接口转发
location /api/ {
proxy_pass http://127.0.0.1:48082/api/;
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;
}
# 前端单页应用刷新防 404
location / {
try_files $uri $uri/ /index.html;
}
}
九、前端配置和 Nginx 配置要一一对应
这是最重要的一点之一。
管理后台
如果前端写的是:
env
VITE_API_URL=/admin-api
那么 Nginx 就必须写:
nginx
location /admin-api/ {
proxy_pass http://127.0.0.1:48082/admin-api/;
}
门户网站
如果前端写的是:
js
baseURL: '/api'
那么 Nginx 就必须写:
nginx
location /api/ {
proxy_pass http://127.0.0.1:48082/api/;
}
不能乱配
比如前端写的是:
js
baseURL: '/api'
但 Nginx 却只配了:
nginx
location /admin-api/
那前端请求 /api/... 时,Nginx 根本接不住,请求自然就失败了。
所以一定要记住一句话:
前端 baseURL 写什么,Nginx 就要代理什么。
十、后端最好只监听本机
既然前端已经通过 Nginx 转发到后端,那么后端就没必要再对公网暴露。
例如 Spring Boot 可以这样配置:
yaml
server:
port: 48082
address: 127.0.0.1
这样意味着:
- 外部用户无法直接访问
48082 - 只能通过 Nginx 代理访问
- 更安全,也更规范
十一、为什么页面刷新会 404
很多 Vue 项目部署后,进入某个页面没事,但一刷新就 404。
例如访问:
text
https://admin.example.com/system/user
前端路由能正常展示。
但刷新时,浏览器会直接请求服务器上的 /system/user 文件。
服务器发现没有这个真实文件,就返回 404。
解决办法就是:
nginx
location / {
try_files $uri $uri/ /index.html;
}
这句的意思是:
- 先找真实文件
- 再找真实目录
- 都没有就返回
index.html
然后再由 Vue Router 接管页面路由。
十二、常见错误排查
1. 前端还在请求 localhost
说明前端配置没有改对,或者改完后没有重新打包。
重点检查:
芋道后台
env
VITE_BASE_URL=''
VITE_API_URL=/admin-api
门户网站
js
baseURL: '/api'
2. 页面正常,接口 404
说明 Nginx 没有正确代理接口路径。
检查:
- 前端写的是
/admin-api还是/api - Nginx 有没有对应的
location proxy_pass路径是否一致- 后端服务是否真的运行在
127.0.0.1:48082
3. 刷新页面 404
说明没有写:
nginx
try_files $uri $uri/ /index.html;
4. 接口返回的是 HTML 页面
这通常说明接口请求没有匹配到代理规则,而是被:
nginx
location / {
try_files $uri $uri/ /index.html;
}
接住了。
本质上就是接口代理规则写错了,或者顺序有问题。
5. 改了 Nginx 配置但没有生效
每次改完配置后,都要执行:
bash
nginx -t
检查配置语法。
没问题后再执行:
bash
nginx -s reload
重新加载配置。
十三、最容易理解的一套最终方案
管理后台前端配置
env
VITE_BASE_URL=''
VITE_API_URL=/admin-api
管理后台 Nginx
nginx
location /admin-api/ {
proxy_pass http://127.0.0.1:48082/admin-api/;
}
门户网站前端配置
js
export const config = {
baseURL: '/api',
default_headers: 'application/json'
};
门户网站 Nginx
nginx
location /api/ {
proxy_pass http://127.0.0.1:48082/api/;
}
最终效果
管理后台
浏览器请求:
text
https://admin.example.com/admin-api/system/captcha/get
Nginx 转发到:
text
http://127.0.0.1:48082/admin-api/system/captcha/get
门户网站
浏览器请求:
text
https://www.example.com/api/user/info
Nginx 转发到:
text
http://127.0.0.1:48082/api/user/info
十四、总结
芋道项目和门户网站在部署时,不要让前端直接写真实后端地址,更不要写 localhost。
正确方式是:
- 前端请求自己的域名
- 接口使用相对路径,例如
/admin-api、/api - Nginx 负责把请求转发到真正的后端服务
- 前端路由通过
try_files解决刷新 404 - 后端只监听
127.0.0.1,不直接暴露公网
一句话总结:
前端只认自己的域名,后端由 Nginx 在服务器内部转发。
十五、可直接复制的最简模板
管理后台前端配置
env
VITE_BASE_URL=''
VITE_API_URL=/admin-api
管理后台 Nginx
nginx
server {
listen 80;
server_name admin.example.com;
root /www/wwwroot/admin-web/dist;
index index.html;
location /admin-api/ {
proxy_pass http://127.0.0.1:48082/admin-api/;
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;
}
location / {
try_files $uri $uri/ /index.html;
}
}
门户网站前端配置
js
export const config = {
baseURL: '/api',
default_headers: 'application/json'
};
门户网站 Nginx
nginx
server {
listen 80;
server_name www.example.com;
root /www/wwwroot/portal-web/dist;
index index.html;
location /api/ {
proxy_pass http://127.0.0.1:48082/api/;
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;
}
location / {
try_files $uri $uri/ /index.html;
}
}