项目采用 SPA 前端 + 后端 API,Nginx 统一作为 HTTPS 主入口和反向代理。
目录
[1️⃣ 背景](#1️⃣ 背景)
[2️⃣ 问题分析](#2️⃣ 问题分析)
[3️⃣ 实战经验(解决方案)](#3️⃣ 实战经验(解决方案))
[① 统一 API 前缀](#① 统一 API 前缀)
[② Nginx 配置优化](#② Nginx 配置优化)
[③ 预检请求(OPTIONS)与跨域](#③ 预检请求(OPTIONS)与跨域)
[④ 安全加固](#④ 安全加固)
[4️⃣ 总结](#4️⃣ 总结)
1️⃣ 背景
系统架构示意图
浏览器
│
▼
HTTPS 18** (这里是你的nginx配置端口)
├─ /api/ → 后端 API (localhost:7500)
└─ / → SPA 前端页面 (/home/***/dist/index.html)
在实际项目中,系统存在如下特点:
-
前端 SPA(Vue/React)部署在 Nginx 下
-
后端服务(ZLMediaKit、Spring Boot、Node.js 等)提供 REST API
-
API 路径有
/api/...
前缀,但前端路由可能有/index/api/...
-
端口是 HTTPS 入口,同时作为 web 主入口
问题:
- 浏览器发起跨域请求时,部分路径被前端兜底 HTML 页面"吃掉",导致 CORS 错误或安全风险。
2️⃣ 问题分析
原理:
-
Nginx 匹配
location
的优先级:-
=
精确匹配 -
^~
前缀匹配 -
普通前缀
/
-
正则
~
-
-
Vue/React SPA 的前端路由常用
location /
兜底返回index.html
-
导致
/index/api/...
路径落到前端,而不是代理到后端 → 返回 HTML请求 /index/api/getSnap → Nginx / (兜底) → 返回 HTML ❌
请求 /api/getSnap → Nginx /api/ → 返回 JSON ✅
风险:
-
路径混淆漏洞:攻击者可访问 API 变种路径
-
绕过鉴权或限流
-
前端错误暴露(返回 HTML 而非 JSON)
3️⃣ 实战经验(解决方案)
① 统一 API 前缀
-
所有后端接口统一挂在
/api/
下 -
禁止
/index/api
或其他变种
② Nginx 配置优化
-
API 路径强制代理到后端:
location ^~ /api/ {
proxy_pass http://localhost:7500;
proxy_set_header Host host; proxy_set_header X-Real-IP remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
} -
前端兜底只兜非 API 路径:
location / {
root /home/***/***t; 这里你的前端路径
index index.html;
try_files $uri /index.html;
}
③ 预检请求(OPTIONS)与跨域
-
API 跨域由后端处理,避免 Nginx 自己返回 204 造成 header 丢失
-
统一允许自定义 header:
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' 'https://..com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With, app-key, app-secret' always;
add_header 'Access-Control-Max-Age' 3600;
return 204;
}
④ 安全加固
-
限流:
limit_req zone=req_limit_per_ip burst=10 nodelay;
-
禁止爬虫访问:通过 User-Agent 屏蔽
-
防止路径绕过攻击:明确 API 与前端分离
4️⃣ 总结
-
原则:API 与前端严格分开 → 避免路径混淆
-
跨域:由后端统一处理 → 避免 Nginx 破坏 CORS header
-
安全:限流、UA 黑名单、防止 OPTIONS 被滥用
实战中,这种细节问题经常导致 CORS 错误、HTML 泄露和安全漏洞。提前规划 API 路径和 Nginx 匹配规则,可以大大降低攻击面。