【部署实战】一文搞懂!SpringBoot + 前端项目上线,Nginx 到底是怎么做请求转发的?
文章目录
📌 前言
很多刚接触项目部署的小伙伴,在本地开发时往往是这样操作的:前端跑在 localhost:3000(或者 80 端口),后端跑在 localhost:8080,中间搞个跨域配置,玩得不亦乐乎。
但是一到服务器部署,面对 Nginx 就懵圈了:
“平时我访问
localhost:8080/index.html才能看到页面,配置了 Nginx 代理后,凭什么我直接敲个域名或localhost就能直接访问了?内部到底是怎么转发的?”
这个问题非常经典,是每个后端/全栈开发必经的“顿悟时刻”。今天,我们就用大白话把 Nginx 转发的底层逻辑扒个底朝天!
🧠 一、先搞懂一个关键概念:反向代理
1.1 没有 Nginx 时,你是怎么访问的?
你在浏览器里输入 localhost:8080/index.html,这意味着:
- 你 直接 和 Spring Boot 内嵌的 Tomcat 对话
- 你 必须知道 它住在
8080这个"房间号" - 如果前端部署在另一个端口(比如
3000),还会遇到 跨域问题
🏠 类比一下:就像你要找某个公司的张三,你得自己知道他在 18楼 808室,然后自己坐电梯上去敲门。

1.2 有了 Nginx 后,发生了什么?
Nginx 就像公司大楼的 前台接待员:
- 你只需要走到 大门口(
localhost,默认80端口) - 前台会根据你的来意,自动帮你转接:
- 想看网页?→ 直接给你拿前端文件
- 想调接口?→ 帮你转接到后端
8080
- 你全程不需要知道任何"房间号"
这就是所谓的 反向代理(Reverse Proxy)——客户端不直接访问真实服务,而是通过一个中间层(Nginx)来统一调度。
1.3 一张图看懂整体流程
┌─────────────────────────────────────────┐
│ Nginx(监听 80 端口) │
│ │
浏览器 │ 请求路径 转发目标 │
──────────────────► │ / ──► 前端静态文件(本地磁盘)│
http://localhost/ │ /api/** ──► Spring Boot(8080) │
http://localhost/api/* │ │
└─────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ /var/www/ │ │ localhost:8080 │
│ html/my-app │ │ (Spring Boot) │
│ index.html │ │ /api/user/1 │
│ css/js/img │ │ /api/order/... │
└──────────────┘ └──────────────────┘

🔧 二、两种部署方式下的 Nginx 配置详解
实际项目中,前端和后端的"打包方式"不同,Nginx 的配置也不一样。下面分两种情况讲清楚。
2.1 方式一:前后端分离部署(⭐ 生产环境推荐)
这是目前最主流的部署方式:
| 组件 | 打包方式 | 部署位置 |
|---|---|---|
| 前端(Vue/React) | npm run build → 生成 dist 目录 |
复制到服务器 /var/www/html/my-app |
| 后端(Spring Boot) | mvn package → 生成 .jar 文件 |
java -jar app.jar,监听 8080 |
| Nginx | — | 监听 80 端口,负责"调度" |

✅ Nginx 配置文件
# /etc/nginx/conf.d/my-app.conf
server {
# ① 监听 80 端口(HTTP 默认端口,浏览器输入域名时自动走这里)
listen 80;
server_name localhost; # 生产环境换成你的域名,如 www.example.com
# ② 前端静态资源:所有"非 API"请求都走这里
location / {
# 前端打包文件存放的目录
root /var/www/html/my-app;
# 关键配置!按顺序尝试:
# 1. 精确匹配文件(如 /css/style.css)
# 2. 匹配目录
# 3. 都没有?回退到 index.html(SPA 单页应用的路由全靠这个!)
try_files $uri $uri/ /index.html;
}
# ③ 后端 API 代理:所有以 /api/ 开头的请求转发给 Spring Boot
location /api/ {
# 核心指令!把请求转发到后端服务
proxy_pass http://localhost:8080;
# 透传客户端真实信息给后端(推荐加上)
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;
}
# ④ 错误页面(可选)
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
🔍 请求流程拆解
场景1:用户打开首页
用户输入:http://localhost/
→ 浏览器发起请求:GET /
→ Nginx 匹配到 location /
→ 去 /var/www/html/my-app 目录找 index.html
→ 找到了!直接返回给浏览器 ✅
→ 浏览器渲染页面,同时加载 CSS/JS 等静态资源(同理走 location /)
场景2:前端页面调用后端接口
前端代码:axios.get('/api/user/1')
→ 浏览器发起请求:GET http://localhost/api/user/1
→ Nginx 匹配到 location /api/(更精确,优先匹配)
→ Nginx 转发请求到 http://localhost:8080/api/user/1
→ Spring Boot 处理请求,返回 JSON 数据
→ Nginx 拿到响应,原样返回给浏览器 ✅
💡 注意:整个过程中,浏览器只和
localhost:80(Nginx)通信,完全不知道8080的存在。所以从浏览器视角看,前端页面和 API 接口是 同源 的 —— 跨域问题自然就不存在了!

2.2 方式二:前后端一体化部署(适合小项目/学习)
有些项目会把前端的 HTML/CSS/JS 直接放在 Spring Boot 的 src/main/resources/static 目录下,然后一起打成一个 JAR 包。
此时 Spring Boot 既当爹又当妈:既处理 API 请求,又提供静态页面。
Nginx 的工作就简单多了 —— 当个"传话筒",所有请求无脑转发就行:
✅ Nginx 配置文件
server {
listen 80;
server_name localhost;
# 简单粗暴:所有请求一律转发给 Spring Boot
location / {
proxy_pass http://localhost:8080;
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;
}
}
🔍 请求流程拆解
用户输入:http://localhost/
→ Nginx 收到请求 GET /
→ 转发到 http://localhost:8080/
→ Spring Boot 内嵌的 Tomcat 处理
→ 返回 static/index.html 的内容
→ Nginx 透传给浏览器 ✅
前端调用:axios.get('/api/user/1')
→ Nginx 收到请求 GET /api/user/1
→ 转发到 http://localhost:8080/api/user/1
→ Spring Boot 的 @RestController 处理
→ 返回 JSON
→ Nginx 透传给浏览器 ✅

⚖️ 三、两种方式对比,怎么选?
| 对比维度 | 🥇 前后端分离部署 | 🥈 前后端一体化部署 |
|---|---|---|
| Nginx 职责 | 静态文件托管 + API 转发 | 纯转发(传话筒) |
| Spring Boot 职责 | 只管 API 业务逻辑 | API + 静态文件,啥都管 |
| 静态资源性能 | ⚡ 极快(Nginx 天生擅长) | 🐢 较慢(Tomcat 处理静态文件效率低) |
| 部署灵活性 | ✅ 前后端独立部署、独立更新 | ❌ 改个前端样式都要重新打 JAR 包 |
| 扩展性 | ✅ 前后端可独立扩容 | ❌ 只能整体扩容 |
| 配置复杂度 | 稍复杂(需区分路径) | 非常简单 |
| 适用场景 | 生产环境、团队协作 | 个人小项目、学习练手 |
📢 结论:生产环境强烈推荐 前后端分离部署。Nginx 处理静态资源的能力是 Tomcat 的 几十倍,而且前后端解耦后,团队协作效率会大幅提升。

🎯 四、关于跨域问题"自动消失"的原理
很多同学好奇:为什么配了 Nginx 之后,前端调后端接口不再报跨域错误了?
这就要理解浏览器的 同源策略(Same-Origin Policy):
同源 = 协议 + 域名 + 端口 三者完全一致
| 场景 | 前端地址 | API 地址 | 是否同源 |
|---|---|---|---|
| 没有 Nginx | http://localhost:3000 |
http://localhost:8080/api |
❌ 端口不同,跨域! |
| 有了 Nginx | http://localhost (80) |
http://localhost/api (80) |
✅ 完全同源,没问题! |
关键在于:浏览器看到的始终是
localhost:80,它根本不知道/api的请求被 Nginx 偷偷转发到了8080。在浏览器眼里,一切都是同一个"源"。

🧩 五、补充:proxy_set_header 那几行到底在干嘛?
你可能注意到配置里总有这么几行"透传头信息"的代码,很多人直接复制粘贴但不理解它的作用:
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;
| 指令 | 作用 | 不加会怎样? |
|---|---|---|
Host $host |
把原始请求的域名传给后端 | 后端拿到的 Host 可能是 localhost:8080,而不是用户实际访问的域名 |
X-Real-IP |
把客户端真实 IP 传给后端 | 后端通过 request.getRemoteAddr() 拿到的是 Nginx 的 IP(127.0.0.1),而非用户真实 IP |
X-Forwarded-For |
记录整个代理链路的 IP | 多层代理时无法追溯请求来源 |
X-Forwarded-Proto |
告诉后端原始请求是 HTTP 还是 HTTPS | 后端无法正确判断协议,可能影响重定向逻辑 |
🔑 一句话总结:这几行配置确保后端能获取到 用户的真实信息,而不是 Nginx 代理服务器的信息。生产环境 务必加上。
✍️ 总结
整篇文章的核心其实就一句话:
Nginx 反向代理 = 在浏览器和后端服务之间加了一个"智能前台",统一入口、按路径分发、隐藏内部细节。
再回顾一下关键要点:
listen 80—— 让 Nginx 监听默认端口,用户无需输入端口号location /—— 匹配前端静态资源请求location /api/—— 匹配后端 API 请求proxy_pass—— 核心指令,实现请求转发try_files—— SPA 单页应用的救命配置,保证前端路由正常工作- 同源效果 —— 浏览器只看到
80端口,跨域问题自然消失
更多推荐


所有评论(0)