Nginx 反向代理前端与后端:完整调用流程
1. 背景
在典型的前后端分离项目中,前端通常使用 Vue、React
等框架开发,构建后生成静态文件;后端则由 Spring Boot、Node.js、Go
等服务提供 API。
生产环境中经常使用 Nginx 作为统一入口:
- 对外暴露 HTTP/HTTPS 服务;
- 提供前端静态资源;
- 将
/api/等接口请求反向代理到后端服务; - 隐藏后端真实地址和端口;
- 统一处理 HTTPS、访问日志、超时、请求头等配置。
本文以以下部署结构为例:
Browser | | https://www.example.com v +-------------------+ | Nginx | | :80/443 | +---------+---------+ | +---- /、/assets/* ------> Frontend Static Files | index.html / JS / CSS | +---- /api/* ------------> Backend Service Spring Boot :8080核心理解:对于浏览器端 SPA(Vue/React)来说,并不是"Nginx
把请求先转给前端服务,再由前端服务调用后端"。前端静态文件下载到浏览器后,JavaScript
在浏览器中执行;浏览器再次向 Nginx 发起 API 请求,Nginx 再将 API
请求代理到后端。
2. 示例部署
假设系统部署如下:
Domain: https://www.example.com Nginx: 80 / 443 Frontend: /var/www/frontend Backend: http://127.0.0.1:8080 API Prefix: /api/前端构建产物:
/var/www/frontend/ ├── index.html └── assets/ ├── index.js └── index.css一个典型的 Nginx 配置:
server { listen 80; server_name www.example.com; # Frontend location / { root /var/www/frontend; try_files $uri $uri/ /index.html; } # Backend API location /api/ { proxy_pass http://127.0.0.1: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; } }前端请求:
axios.get('/api/users')3. 完整调用流程
整个流程可以拆成两个阶段:
- 浏览器获取并加载前端页面;
- 浏览器中的前端 JavaScript 调用后端 API。
完整链路:
┌─────────────┐ │ Browser │ └──────┬──────┘ │ │ ① GET / ▼ ┌─────────────┐ │ Nginx │ └──────┬──────┘ │ │ ② 查找前端静态资源 ▼ /var/www/frontend/index.html │ │ ③ 返回 HTML ▼ ┌─────────────┐ │ Browser │ └──────┬──────┘ │ │ ④ GET /assets/index.js │ GET /assets/index.css ▼ ┌─────────────┐ │ Nginx │ └──────┬──────┘ │ │ ⑤ 返回 JS/CSS ▼ ┌─────────────────────────┐ │ Browser executes JS │ │ Vue / React application │ └────────────┬────────────┘ │ │ ⑥ GET /api/users ▼ ┌─────────────┐ │ Nginx │ └──────┬──────┘ │ │ ⑦ proxy_pass ▼ ┌─────────────┐ │ Backend │ │ :8080 │ └──────┬──────┘ │ │ ⑧ JSON Response ▼ ┌─────────────┐ │ Nginx │ └──────┬──────┘ │ │ ⑨ JSON Response ▼ ┌─────────────┐ │ Browser │ └──────┬──────┘ │ │ ⑩ Vue/React 更新状态和 DOM ▼ 页面更新4. 第一阶段:浏览器访问前端
4.1 用户输入域名
用户访问:
https://www.example.com/经过 DNS 解析后,请求到达服务器上的 Nginx。
请求可以简化为:
GET / HTTP/1.1 Host: www.example.com4.2 Nginx 匹配location /
Nginx 配置:
location / { root /var/www/frontend; try_files $uri $uri/ /index.html; }请求 URI 为:
/因此进入location /。
对于 SPA 项目,try_files很重要:
try_files $uri $uri/ /index.html;其含义可以理解为:
先查找请求对应的文件 | +-- 文件存在 --> 直接返回 | +-- 目录存在 --> 使用目录 | +-- 都不存在 --> 返回 /index.html这使 Vue Router、React Router 使用 History 模式时,即使直接访问:
/user/1001Nginx 也可以回退到:
/index.html之后由前端路由决定展示哪个页面。
5. 第二阶段:浏览器加载 JS 和 CSS
浏览器拿到index.html后,会解析其中的资源,例如:
<scripttype="module"src="/assets/index.js"></script><linkrel="stylesheet"href="/assets/index.css">浏览器继续发送请求:
GET /assets/index.js GET /assets/index.css请求仍然是:
Browser | v NginxNginx 找到对应静态文件后直接返回:
Browser | | GET /assets/index.js v Nginx | | /var/www/frontend/assets/index.js v Browser此时 Vue/React 的 JavaScript 才真正开始在用户浏览器中运行。
6. 第三阶段:前端调用后端 API
假设页面初始化后需要查询用户列表:
axios.get('/api/users')这段代码运行的位置是浏览器,而不是 Nginx,也不是服务器上的前端目录。
因此实际产生的是新的 HTTP 请求:
GET /api/users HTTP/1.1 Host: www.example.com完整地址实际上是:
https://www.example.com/api/users调用方向:
Browser | | GET /api/users v Nginx7. 第四阶段:Nginx 将 API 请求反向代理到后端
Nginx 收到:
/api/users由于配置了:
location /api/ { proxy_pass http://127.0.0.1:8080/; }因此该请求匹配/api/,不会按照前端静态资源处理,而是执行反向代理。
在这个配置下,请求大致变成:
Browser | | GET /api/users v Nginx | | GET /users v 127.0.0.1:8080后端只需要监听内部端口:
127.0.0.1:8080客户端不需要知道后端真实 IP、端口或内部拓扑。
8. 第五阶段:后端处理业务
以 Spring Boot 为例,如果 Nginx 将/api/users转换成/users,后端可以定义:
@RestControllerpublicclassUserController{@GetMapping("/users")publicList<User>users(){returnuserService.list();}}后端内部可能继续执行:
Controller | v Service | v Repository / Mapper | v Database例如:
Nginx | v Controller | v UserService | v UserMapper | v MySQL数据库查询结束后,数据逐层返回,最终由后端序列化为 JSON:
[{"id":1,"name":"Tom"},{"id":2,"name":"Jerry"}]9. 第六阶段:响应原路返回
后端不会直接把响应发送给浏览器。
由于 HTTP 连接是 Nginx 与后端建立的,因此响应首先回到 Nginx:
Backend | | HTTP Response + JSON v Nginx然后 Nginx 再把响应发送给浏览器:
Backend | | JSON v Nginx | | JSON v Browser因此完整 API 链路是:
Browser | | GET /api/users v Nginx | | GET /users v Backend | | Query v Database | | Result v Backend | | JSON v Nginx | | JSON v Browser浏览器中的 Vue/React 获取 JSON 后更新状态,最终重新渲染页面。
10. 一张图理解整个系统
Internet | v +-------------------+ | Nginx | | :80/443 | +---------+---------+ | +-------------+-------------+ | | location / location /api/ | | v v +-------------------+ +-------------------+ | Frontend Static | | Backend Service | | index.html | | Spring Boot | | JS / CSS | | :8080 | +-------------------+ +---------+---------+ | v +-------------------+ | Database | | MySQL | +-------------------+从浏览器角度看:
GET https://www.example.com/ | +--> Nginx --> index.html GET https://www.example.com/assets/index.js | +--> Nginx --> index.js GET https://www.example.com/api/users | +--> Nginx --> Backend --> DatabaseNginx 根据请求 URI 完成流量分发。
11.proxy_pass最容易踩的坑:末尾/
这是 Nginx 反向代理配置中非常重要的一点。
情况 A:proxy_pass带/
location /api/ { proxy_pass http://127.0.0.1:8080/; }客户端请求:
/api/users通常转发为:
http://127.0.0.1:8080/users也就是说,匹配到的/api/前缀被替换为/。
情况 B:proxy_pass不带/
location /api/ { proxy_pass http://127.0.0.1:8080; }客户端请求:
/api/users通常转发为:
http://127.0.0.1:8080/api/users可以简单记忆:
location /api/ { proxy_pass http://backend/; } ^ 有 / /api/users ----------> /users而:
location /api/ { proxy_pass http://backend; } ^ 无 / /api/users ----------> /api/users因此配置 Nginx 时,必须和后端 Controller 的路径设计保持一致。
12. 为什么这种方式通常没有 CORS 问题
假设不使用 Nginx 统一代理,前端页面是:
https://www.example.com而前端直接访问:
http://api.example.com:8080/users浏览器看到的是两个不同的 Origin,可能涉及跨域资源共享(CORS)配置。
使用 Nginx 后,浏览器只看到:
https://www.example.com/ https://www.example.com/api/users两者协议、主机和端口一致,因此从浏览器视角属于同源请求。
需要注意:这是因为浏览器请求的是 Nginx 的/api/...地址。Nginx
在服务器内部继续访问127.0.0.1:8080不属于浏览器跨域检查的范畴。
13. 请求头与真实客户端 IP
Nginx 作为反向代理后,后端直接建立连接的一方是 Nginx,而不是用户浏览器。
因此通常会配置:
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;常见含义:
Header 作用
Host保留客户端访问的 HostX-Real-IP传递客户端 IPX-Forwarded-For记录代理链上的客户端 IPX-Forwarded-Proto告诉后端原始请求是 HTTP 还是 HTTPS
链路可以理解为:
Browser (Client IP) | v Nginx | | X-Real-IP: Client IP | X-Forwarded-For: Client IP, ... v Backend后端是否以及如何信任这些
Header,应结合实际代理拓扑和安全配置决定,避免直接信任来自非可信代理的伪造转发头。
14. Nginx 在整个架构中的角色
Nginx 并不是业务代码的一部分,它更像系统入口和流量调度层。
+----------------+ | Client | +-------+--------+ | v +----------------+ | Nginx | +-------+--------+ | +-------------+-------------+ | | | v v v Frontend Backend A Backend B Static /api/user /api/orderNginx 可以承担:
- 静态资源服务器;
- 反向代理;
- HTTPS/TLS 终止;
- API 路由;
- 负载均衡;
- 请求头处理;
- 超时控制;
- 访问日志;
- 压缩和缓存;
- 限流等入口层能力。
15. 常见错误理解
误区 1:前端服务器调用后端
错误理解:
Browser | v Frontend | v Backend对于构建后部署为静态文件的 Vue/React SPA,这通常不准确。
更准确的是:
Browser | +---- GET / ----------> Nginx --> Frontend Static Files | +---- GET /api/users -> Nginx --> Backend前端 JavaScript 已经下载到浏览器,并在浏览器中执行。
如果使用 Next.js SSR、Nuxt SSR、BFF
等服务端渲染或服务端中间层架构,则调用链可能不同,不能套用纯 SPA
模型。
误区 2:Nginx 收到所有请求都会交给后端
不是。
Nginx 会根据location匹配规则决定如何处理:
/ ├── /assets/index.js --> static file ├── /login --> index.html (SPA fallback) └── /api/users --> backend误区 3:后端响应直接返回浏览器
使用反向代理时,通常是:
Backend --> Nginx --> Browser而不是:
Backend -------------> BrowserNginx 是这一 HTTP 代理链路中的中间节点。
16. 排查问题时如何定位
遇到"页面正常但接口请求失败"时,可以按照调用链逐层排查:
Browser | | ① 请求是否正确? v Nginx | | ② location 是否匹配? | ③ proxy_pass URI 是否正确? v Backend | | ④ Controller 路径是否正确? | ⑤ 服务是否正常? v Database浏览器层
打开浏览器 DevTools → Network,检查:
Request URL Request Method Status Code Request Headers Response Headers Response Body尤其确认实际请求是不是:
https://www.example.com/api/users而不是意外请求到其他域名或端口。
Nginx 层
检查配置:
nginx-t查看访问日志和错误日志,例如:
access.log error.log重点确认:
/api/users是否进入预期的location /api/。
后端层
确认后端服务端口:
127.0.0.1:8080在服务器内部可以直接验证:
curlhttp://127.0.0.1:8080/users如果直接访问后端成功,而通过:
https://www.example.com/api/users失败,问题通常集中在 Nginx 路由、URI 重写、代理配置或网络连接层。
17. HTTP 状态码快速定位
常见现象可以辅助判断问题所在:
现象 常见排查方向
404URI、location、proxy_pass路径或后端 Controller502 Bad Gateway后端未启动、地址/端口错误、连接失败504 Gateway Timeout后端处理过慢、网络问题、代理超时403Nginx 权限、安全规则或后端鉴权
CORS Error 浏览器实际请求是否仍然跨 Origin
页面刷新 404 SPAtry_files配置
API 返回 HTML API 请求可能被错误地落入前端location
这些只是常见方向,最终仍应结合 Nginx 日志、后端日志和浏览器 Network
信息判断。
18. 最终总结
整个调用过程可以浓缩为两条链路。
前端资源加载
Browser | | GET / v Nginx | | Static File v index.html / JS / CSS | v Browser executes Vue/ReactAPI 调用
Browser JavaScript | | GET /api/users v Nginx | | Reverse Proxy v Backend | | SQL v Database | v Backend | | JSON v Nginx | | JSON v Browser | v Vue/React renders UI一句话概括:
Nginx 是统一入口:前端静态资源请求由 Nginx 直接返回,API 请求由
Nginx 反向代理到后端;前端 JavaScript
实际运行在浏览器中,后端响应再通过 Nginx 原路返回浏览器。
19. 推荐的理解模型
以后看到类似配置:
location / { root /var/www/frontend; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/; }可以直接在脑中转换为:
+--> / --> Frontend | Browser --> Nginx --+ | +--> /api/* --> Backend这就是 Nginx 在典型前后端分离 SPA 架构中的核心调用模型。