切换日光/暗黑模式
006. Nginx 静态资源与反向代理
学习目标
这一节重点理解 Nginx 如何同时服务前端页面、静态资源和后端接口。
学完后,你应该能理解:
- 静态资源目录是什么;
- 为什么前端打包产物可以直接由 Nginx 托管;
- 单页面应用刷新后为什么可能 404;
location、alias、try_files分别解决什么问题;- 为什么要把后端 API 代理到统一入口;
- 如何减少生产环境跨域问题。
静态资源目录
宝塔创建站点后,会生成一个网站根目录。
这个目录可以理解成:
所有静态资源放置的目录。
只要把文件放进这个目录,浏览器就可以通过 HTTP 访问到。
例如目录里有:
txt
1.txt访问:
txt
http://服务器IP/1.txt就能拿到这个文件内容。
HTTP 默认端口是 80,所以:
txt
http://服务器IP实际访问的是服务器的 80 端口。
前端打包产物也是静态资源
React / Vue 前端打包后,产物通常是:
index.html- JS 文件;
- CSS 文件;
- 图片;
- 字体;
- 其他静态资源。
这些文件本身不需要 Node 服务才能运行。只要 Nginx 能把它们返回给浏览器,浏览器就能加载并执行前端应用。
所以前端部署可以理解成:
- 本地执行 build;
- 得到 dist 或类似目录;
- 上传到服务器站点目录;
- Nginx 托管这些静态文件。
访问目录时会找 index.html
当访问一个目录路径时,Nginx 会尝试找这个目录下的入口文件。
例如访问:
txt
http://服务器IP/ai-admin/Nginx 会尝试返回:
txt
/ai-admin/index.html这就是为什么前端打包目录里必须有 index.html。
单页面应用刷新 404
React / Vue 单页面应用的路由很多时候不是服务器真实文件。
例如页面里跳转到:
txt
/ai-admin/pages/user这个地址在前端路由里存在,但服务器目录里未必真的有:
txt
/ai-admin/pages/user/index.html如果直接刷新浏览器,Nginx 会按真实文件路径去找,找不到就可能 404。
这不是前端页面坏了,而是单页面应用需要服务器把未知路由回退到 index.html。
location 是一次匹配规则
Nginx 里的 location 可以理解成“当请求路径匹配某个前缀时,按这段规则处理”。
例如:
nginx
location /ai-admin/ {
alias /www/wwwroot/web/ai-admin/;
try_files $uri $uri/ /ai-admin/index.html;
}这里表达的是:
- 请求以
/ai-admin/开头; - 实际去服务器目录
/www/wwwroot/web/ai-admin/找文件; - 如果真实文件不存在,就回退到
/ai-admin/index.html。
alias 是路径别名
alias 用来把 URL 路径映射到服务器文件目录。
浏览器访问:
txt
/ai-admin/assets/index.cssNginx 可以去真实目录里找:
txt
/www/wwwroot/web/ai-admin/assets/index.css这就是 URL 路径和服务器文件路径之间的转换。
try_files 解决前端刷新问题
try_files 的核心作用是:按顺序尝试找文件。
例如:
nginx
try_files $uri $uri/ /ai-admin/index.html;可以理解成:
- 先找请求路径对应的文件;
- 再找请求路径对应的目录;
- 都没有,就返回前端入口
index.html。
这样刷新一个前端路由时,Nginx 不会直接报 404,而是把请求交给前端应用,由 React Router 或 Vue Router 决定显示哪个页面。
静态文件访问
除了前端页面,系统还会有上传文件,例如发票图片。
这些文件也可以先放在服务器目录里,由 Nginx 暴露访问路径。
例如:
txt
/upload/file/xxx.png对应服务器某个上传目录下的图片。
学习阶段先直接托管这些文件。更复杂的权限控制,例如签名 URL、对象存储权限、私有文件访问,后面再深入。
直接访问根路径
如果前端必须访问:
txt
http://服务器IP/ai-admin/分享给别人时不够友好。
可以在 Nginx 里把根路径重定向到前端入口:
txt
/ -> /ai-admin/这样别人只访问:
txt
http://服务器IP也能进入前端页面。
代理后端 API
如果前端走 80 端口,后端直接暴露 7003 端口,浏览器会认为它们不是同源。
更好的方式是让前端请求:
txt
/api再由 Nginx 转发到后端真实端口:
txt
/api -> http://127.0.0.1:7003这样浏览器看到的仍然是同一个协议、同一个 IP 或域名、同一个端口。
WebSocket 也需要代理
后续实时语音识别会用到 WebSocket。
WebSocket 和普通 HTTP 请求不完全一样,Nginx 代理时要额外处理连接升级相关请求头。
当前先记住:如果一个功能需要实时通信,比如语音识别流式返回、实时消息推送,就不能只按普通 HTTP 请求理解。
前端生产环境 API 地址
前端本地开发时,可能会直接请求:
txt
http://服务器IP:7003生产构建时,要改成走统一路径,例如:
txt
/api然后重新 build,再上传新的打包产物。
这样部署后的请求路径就会变成:
txt
http://服务器IP/api/xxx浏览器看到的是同源请求,跨域问题自然减少。
生产环境跨域策略
当 Nginx 已经统一了前端、后端和静态资源入口后,后端就不应该继续无条件允许所有跨域。
可以按环境区分:
| 环境 | 跨域策略 |
|---|---|
| 本地开发 | 可以适当放开,方便调试 |
| 测试环境 | 限制到测试前端地址 |
| 生产环境 | 尽量同源访问,只允许必要来源 |
部署不是只让页面能打开,还要让访问方式接近真实企业项目。
多环境代理
同一台服务器上可能同时有开发环境和测试环境。
它们后端端口不能相同,例如:
txt
开发后端:7003
测试后端:7004可以用 Nginx 路径区分:
txt
/dev-api -> 7003
/test-api -> 7004也可以用不同域名区分。当前学习阶段先掌握同一个 IP 下按路径代理的方式。