Nginx + Vue Router 基础:SPA History 部署、反向代理与请求分流

Nginx + Vue Router 基础:SPA History 部署、反向代理与请求分流

适用场景:Vue 3 / Vite / Vue Router 4 / Node.js / Koa / Nginx

示例域名:www.test.com

文档定位:偏实战,适合前端、Node.js 开发在服务器部署 SPA 项目时查阅

建议 CSDN 标签:NginxVue3Vue RouterVite前端部署


前言

前端项目部署到服务器后,经常会遇到这些问题:

  • Vue Router history 模式刷新页面后 404;
  • /oa/oa/ 到底有什么区别;
  • rootalias 经常分不清;
  • 明明改了 Nginx 配置,却始终不生效;
  • /api/ 代理到 Node.js 后接口路径不对;
  • 想让移动端访问 H5、PC 访问 PC 页面;
  • 需要根据请求头、参数、User-Agent 做简单分流;
  • 403、404、502 出现以后,不知道应该先查哪里。

本文不追求覆盖 Nginx 的全部能力,而是集中讲清楚 前端 / Node.js 开发最常用、最容易出错的一部分


1. Nginx 的整体请求流程

先建立一个最重要的心智模型:

text 复制代码
浏览器请求
    │
    ▼
Nginx
    │
    ▼
根据域名寻找 server
    │
    ▼
根据 URI 寻找 location
    │
    ├───────────────┬────────────────┬────────────────┐
    ▼               ▼                ▼                ▼
静态文件          反向代理          重定向          拒绝/错误
root / alias      proxy_pass        return           deny / error_page
    │               │
    ▼               ▼
index/try_files    Node/Koa/Java 等后端

例如访问:

text 复制代码
https://www.test.com/oa/user/list

可能经历:

text 复制代码
www.test.com
    ↓
找到对应 server
    ↓
URI = /oa/user/list
    ↓
匹配 location /oa/
    ↓
尝试读取真实文件
    ↓
文件不存在
    ↓
try_files 回退 /oa/index.html
    ↓
Vue 启动
    ↓
Vue Router 接管 /user/list

可以把 Nginx 简单理解成:

根据域名、URL、请求头、设备、参数等信息,决定请求下一步去哪里的"请求分流器"。


2. Nginx 配置层级:httpserverlocationinclude

一个常见的拆分结构:

text 复制代码
/etc/nginx/nginx.conf
        │
        └── http
             │
             ├── include server/www/*.conf
             │
             ├── include server/api/*.conf
             │
             └── include server/admin/*.conf
                         │
                         └── server
                              │
                              └── include location/www/*.conf
                                      │
                                      ├── location /oa/
                                      ├── location /crm/
                                      └── location /api/

示例:

nginx 复制代码
http {
    include /etc/nginx/conf.d/server/www/*.conf;
}

被加载的 server

nginx 复制代码
server {
    listen 443 ssl;
    server_name www.test.com;

    include /etc/nginx/conf.d/location/www/*.conf;
}

再继续加载:

nginx 复制代码
location /oa/ {
}

location /crm/ {
}

location /api/ {
}

include 最重要的理解

include 不是注释,也不是"参考配置"。

它的含义是:

把指定文件里的配置真正加载到当前位置。

如果缺少:

nginx 复制代码
include /etc/nginx/conf.d/server/www/*.conf;

那么即使 www.test.comserver 文件本身写得完全正确,也不会生效。

请求链路

text 复制代码
nginx.conf
    ↓
include server/*.conf
    ↓
server_name www.test.com
    ↓
include location/*.conf
    ↓
location /oa/

3. server:根据域名分流

server 可以理解为一个网站入口。

nginx 复制代码
server {
    listen 443 ssl;
    server_name www.test.com;
}

主要处理:

text 复制代码
https://www.test.com/*

另外一个:

nginx 复制代码
server {
    listen 443 ssl;
    server_name blog.test.com;
}

主要处理:

text 复制代码
https://blog.test.com/*

因此:

层级 主要作用
http Nginx HTTP 全局配置
server 根据域名、端口区分网站
location 根据 URI 区分业务
include 加载其他配置文件

4. location:根据 URL 分流

例如:

nginx 复制代码
location /oa/ {
}

可以处理:

text 复制代码
/oa/
/oa/login
/oa/user/list
/oa/system/user

接口:

nginx 复制代码
location /api/ {
}

可以处理:

text 复制代码
/api/login
/api/user
/api/order/list

因此可以记住:

text 复制代码
server   → 根据域名分流
location → 根据 URI 分流

5. /oa/oa/= /oa 的区别

5.1 location /oa

nginx 复制代码
location /oa {
}

这是前缀匹配。

以下 URI 都可能匹配:

text 复制代码
/oa
/oa/
/oa/user
/oa123
/oabcdef

原因是它们都以 /oa 开头。


5.2 location /oa/

nginx 复制代码
location /oa/ {
}

匹配:

text 复制代码
/oa/
/oa/user
/oa/system/user

但不会匹配:

text 复制代码
/oa
/oa123

5.3 location = /oa

nginx 复制代码
location = /oa {
}

= 表示 精确匹配

只匹配:

text 复制代码
/oa

不会匹配:

text 复制代码
/oa/
/oa/user
/oa123

5.4 SPA 推荐组合

nginx 复制代码
location = /oa {
    return 301 /oa/;
}

location /oa/ {
    root /home/html/project;
    index index.html;
    try_files $uri $uri/ /oa/index.html;
}

请求过程:

text 复制代码
/oa
 ↓
精确匹配 location = /oa
 ↓
301
 ↓
/oa/
 ↓
location /oa/
 ↓
进入 SPA

6. location 常见匹配方式与优先级

常见形式:

nginx 复制代码
location = /oa {
}

精确匹配。

nginx 复制代码
location /oa/ {
}

普通前缀匹配。

nginx 复制代码
location ^~ /oa/ {
}

优先前缀匹配。

nginx 复制代码
location ~ \.php$ {
}

正则匹配,区分大小写。

nginx 复制代码
location ~* \.(js|css|png|jpg)$ {
}

正则匹配,不区分大小写。

简化优先级

text 复制代码
① = 精确匹配
        ↓
② 寻找最长前缀 location
        ↓
③ 如果最长前缀带 ^~
        ↓
   直接使用,不再检查正则
        ↓
④ 否则检查正则 ~ / ~*
        ↓
⑤ 第一个命中的正则生效
        ↓
⑥ 没有正则命中
        ↓
   使用之前找到的最长前缀

示例

nginx 复制代码
location ^~ /oa/ {
    ...
}

location ~* \.js$ {
    ...
}

请求:

text 复制代码
/oa/static/app.js

因为 /oa/ 使用了 ^~,所以会优先进入 /oa/,不会继续被 .js 正则 location 抢走。


7. root 的工作原理

示例:

nginx 复制代码
location /oa/ {
    root /home/html/project;
}

访问:

text 复制代码
/oa/index.html

实际文件:

text 复制代码
/home/html/project/oa/index.html

可以记公式:

text 复制代码
真实路径 = root + 完整 URI

也就是:

text 复制代码
/home/html/project
+
/oa/index.html
=
/home/html/project/oa/index.html

如果目录结构:

text 复制代码
/home/html/project/
├── oa/
│   ├── index.html
│   └── assets/
└── crm/
    ├── index.html
    └── assets/

那么:

nginx 复制代码
location /oa/ {
    root /home/html/project;
}

location /crm/ {
    root /home/html/project;
}

非常自然。


8. alias 的工作原理

例如:

nginx 复制代码
location /download/ {
    alias /data/project/files/;
}

用户访问:

text 复制代码
/download/a.zip

可以理解成把:

text 复制代码
/download/

替换为:

text 复制代码
/data/project/files/

最终访问:

text 复制代码
/data/project/files/a.zip

9. rootalias 对比

对比项 root alias
核心逻辑 根目录 + 完整 URI 用实际目录替换 location 前缀
URL 和磁盘目录一致 推荐 可以但没必要
URL 和磁盘目录不同 较绕 推荐
SPA 项目 常用 可用但更容易绕
理解难度 较低 较高

记忆口诀

text 复制代码
URL 路径和磁盘目录结构一致
→ root

URL 路径和实际目录完全不同
→ alias

10. index 的作用

nginx 复制代码
index index.html;

表示访问目录时优先寻找默认首页。

例如:

nginx 复制代码
location /oa/ {
    root /home/html/project;
    index index.html;
}

访问:

text 复制代码
/oa/

对应:

text 复制代码
/home/html/project/oa/index.html

11. Vue Router Hash 模式

Vue Router:

ts 复制代码
import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  routes,
})

URL:

text 复制代码
https://www.test.com/oa/#/user/list

关键是:

text 复制代码
#/user/list

# 后面的内容不会作为 HTTP 请求路径发送给服务器。

Nginx 实际看到的主要是:

text 复制代码
/oa/

因此 Hash 模式通常不容易出现"刷新子路由 404"。

Hash 模式流程

text 复制代码
浏览器地址:
/oa/#/user/list
       │
       ▼
Nginx 看到:
/oa/
       │
       ▼
返回 index.html
       │
       ▼
Vue Router 读取:
#/user/list

12. Vue Router History 模式

Vue Router:

ts 复制代码
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory('/oa/'),
  routes,
})

URL:

text 复制代码
https://www.test.com/oa/
https://www.test.com/oa/login
https://www.test.com/oa/user/list

URL 中没有 #,更加自然。

但是 History 模式有一个关键区别:

浏览器刷新时,完整路径会真正请求 Nginx。

例如:

text 复制代码
https://www.test.com/oa/user/list

Nginx 收到:

text 复制代码
GET /oa/user/list

13. 为什么 History 模式刷新会 404

假设用户已经打开:

text 复制代码
/oa/

然后 Vue 执行:

ts 复制代码
router.push('/user/list')

Vue Router 会通过浏览器 History API 修改 URL:

text 复制代码
/oa/user/list

这一步通常不会重新请求新的 HTML。

因此页面正常。

但是用户按 F5

text 复制代码
浏览器
   ↓
GET /oa/user/list
   ↓
Nginx
   ↓
寻找真实文件:
/home/html/project/oa/user/list
   ↓
不存在
   ↓
404

因为:

text 复制代码
/user/list

本质上只是 Vue Router 的前端路由,并不是服务器真实目录。


14. try_files 如何解决 History 刷新 404

标准配置:

nginx 复制代码
location /oa/ {
    root /home/html/project;
    index index.html;

    try_files $uri $uri/ /oa/index.html;
}

核心:

nginx 复制代码
try_files $uri $uri/ /oa/index.html;

可以理解为:

text 复制代码
① 尝试当前 URI 对应文件
       ↓
② 尝试当前 URI 对应目录
       ↓
③ 都不存在
       ↓
④ 返回 /oa/index.html

访问:

text 复制代码
/oa/user/list

Nginx 先查:

text 复制代码
/home/html/project/oa/user/list

不存在。

最终回退:

text 复制代码
/oa/index.html

对应真实文件:

text 复制代码
/home/html/project/oa/index.html

然后:

text 复制代码
Nginx 返回 index.html
        ↓
Vue 启动
        ↓
Vue Router 读取当前浏览器地址
        ↓
createWebHistory('/oa/')
        ↓
去掉 /oa/ base
        ↓
内部路由 = /user/list
        ↓
显示 UserList 页面

这就是 Vue History 模式部署最核心的一条链。


15. Vue Router Base 的作用

ts 复制代码
createWebHistory('/oa/')

其中:

text 复制代码
/oa/

叫 Router Base。

它表示:

当前 Vue 应用部署在网站的 /oa/ 路径下。

例如:

ts 复制代码
router.push('/login')

浏览器实际 URL:

text 复制代码
/oa/login

而不是:

text 复制代码
/login

关系:

浏览器 URL Vue Router 内部路由
/oa/ /
/oa/login /login
/oa/user/list /user/list
/oa/system/user /system/user

因此 routes 中不要重复写 /oa

ts 复制代码
// 正确
{
  path: '/login'
}

而不是:

ts 复制代码
// 不推荐
{
  path: '/oa/login'
}

16. Vite base 和 Vue Router base 的区别

这是 SPA 二级目录部署最容易混淆的两个配置。

Vite

ts 复制代码
export default defineConfig({
  base: '/oa/',
})

主要控制:

  • JS 地址;
  • CSS 地址;
  • 图片地址;
  • 字体;
  • 静态资源基础路径。

打包后可能得到:

html 复制代码
<script src="/oa/assets/index-xxx.js"></script>
<link href="/oa/assets/index-xxx.css" rel="stylesheet">

Vue Router

ts 复制代码
createWebHistory('/oa/')

主要控制:

  • /login
  • /user/list
  • /system/user

这些前端页面路由的基础路径。

对照表

配置 主要职责
Vite base 控制 JS、CSS、图片等静态资源
Router base 控制 Vue Router 页面路由
Nginx root URL 映射到哪个磁盘目录
Nginx try_files 前端路由找不到真实文件时回退 index.html

推荐写法

如果 Vite 已经:

ts 复制代码
base: '/oa/'

Router 可以:

ts 复制代码
createWebHistory(import.meta.env.BASE_URL)

减少重复配置。


17. 一个域名部署多个 Vue SPA

服务器目录:

text 复制代码
/home/html/project/
├── oa/
│   ├── index.html
│   └── assets/
│
└── crm/
    ├── index.html
    └── assets/

Nginx:

nginx 复制代码
location = /oa {
    return 301 /oa/;
}

location /oa/ {
    root /home/html/project;
    index index.html;
    try_files $uri $uri/ /oa/index.html;
}

location = /crm {
    return 301 /crm/;
}

location /crm/ {
    root /home/html/project;
    index index.html;
    try_files $uri $uri/ /crm/index.html;
}

OA:

ts 复制代码
// vite.config.ts
base: '/oa/'
ts 复制代码
createWebHistory('/oa/')

CRM:

ts 复制代码
// vite.config.ts
base: '/crm/'
ts 复制代码
createWebHistory('/crm/')

最终:

text 复制代码
www.test.com
│
├── /oa/
│    └── OA Vue SPA
│
├── /crm/
│    └── CRM Vue SPA
│
└── /api/
     └── 后端服务

18. proxy_pass 反向代理

示例:

nginx 复制代码
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}

浏览器访问:

text 复制代码
https://www.test.com/api/user/list

Nginx 将请求转发给:

text 复制代码
http://127.0.0.1:3000/user/list

这就是反向代理。

请求流程

text 复制代码
浏览器
https://www.test.com/api/user/list
            │
            ▼
          Nginx
            │
            ▼
proxy_pass http://127.0.0.1:3000/
            │
            ▼
Node / Koa / Java 后端

浏览器只知道 www.test.com,并不需要直接访问后端端口。


19. proxy_pass 最后的 / 为什么重要

下面两种配置不同。

/

nginx 复制代码
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}

请求:

text 复制代码
/api/user/list

后端通常收到:

text 复制代码
/user/list

/api/ 被替换掉。


没有 /

nginx 复制代码
location /api/ {
    proxy_pass http://127.0.0.1:3000;
}

请求:

text 复制代码
/api/user/list

后端通常收到:

text 复制代码
/api/user/list

对照

配置 浏览器请求 后端路径
proxy_pass http://127.0.0.1:3000/; /api/user /user
proxy_pass http://127.0.0.1:3000; /api/user /api/user

接口代理后莫名 404 时,这里是重点检查对象。


20. 常用代理请求头

推荐模板:

nginx 复制代码
location /api/ {
    proxy_pass http://127.0.0.1:3000/;

    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 用户访问的域名
X-Real-IP 客户端 IP
X-Forwarded-For 代理链 IP
X-Forwarded-Proto http / https

21. WebSocket 代理

常见配置:

nginx 复制代码
location /ws/ {
    proxy_pass http://127.0.0.1:3000;

    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;
}

核心:

nginx 复制代码
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

流程:

text 复制代码
HTTP 请求
   ↓
Upgrade
   ↓
WebSocket 长连接

22. return 301 / 302 重定向

例如:

nginx 复制代码
return 302 /mobile/;

表示:

浏览器临时跳转到当前域名下的 /mobile/

当前域名:

text 复制代码
https://www.test.com

最终:

text 复制代码
https://www.test.com/mobile/

流程:

text 复制代码
浏览器请求 /
    ↓
Nginx 返回:
302
Location: /mobile/
    ↓
浏览器重新请求
/mobile/

301 与 302

状态码 含义 常见场景
301 永久重定向 域名迁移、永久 URL 变更
302 临时重定向 设备分流、临时页面

例如:

nginx 复制代码
return 302 https://m.test.com$request_uri;

用户访问:

text 复制代码
https://www.test.com/news/1?id=10

会跳:

text 复制代码
https://m.test.com/news/1?id=10

23. returntry_files 的区别

return 302

nginx 复制代码
return 302 /mobile/;

属于浏览器重定向。

text 复制代码
/
 ↓
浏览器收到 302
 ↓
浏览器重新请求
 ↓
/mobile/

地址栏发生变化。


try_files

nginx 复制代码
try_files $uri $uri/ /index.html;

通常属于 Nginx 内部文件选择或内部重定向过程。

浏览器访问:

text 复制代码
/user/list

Nginx 实际可能返回:

text 复制代码
/index.html

但浏览器仍然保持:

text 复制代码
/user/list

对照

指令 是否让浏览器重新请求 地址栏
return 301/302 变化
try_files 通常否 通常不变化

24. rewrite URL 改写

固定跳转优先:

nginx 复制代码
return 301 /new;

如果需要根据原 URL 动态修改:

nginx 复制代码
rewrite ^/old/(.*)$ /new/$1 permanent;

例如:

text 复制代码
/old/user
    ↓
/new/user

其中:

text 复制代码
(.*)

捕获:

text 复制代码
user

$1 就代表捕获的内容。

选择原则

text 复制代码
固定跳转
→ return

需要正则替换 URL
→ rewrite

25. 移动端与 PC 端分流

Nginx 可以读取:

nginx 复制代码
$http_user_agent

简单判断:

nginx 复制代码
if ($http_user_agent ~* "(Android|iPhone|iPad|Mobile)") {
    return 302 https://m.test.com$request_uri;
}

完整示例:

nginx 复制代码
server {
    server_name www.test.com;

    location / {
        if ($http_user_agent ~* "(Android|iPhone|iPad|Mobile)") {
            return 302 https://m.test.com$request_uri;
        }

        root /home/html/pc;
        index index.html;
    }
}

流程:

text 复制代码
请求 www.test.com
      │
      ▼
读取 User-Agent
      │
      ├── Mobile
      │      ↓
      │   302 → m.test.com
      │
      └── PC
             ↓
          返回 PC 页面

注意:User-Agent 可以伪造,不适合做真正的安全认证。

如果一套响应式页面可以解决问题,优先使用响应式。只有 PC/H5 是两套独立站点时,才更适合做服务端分流。


26. map 的使用

条件较多时,比到处写 if 更推荐使用 map

map 通常配置在 http 层:

nginx 复制代码
http {
    map $http_user_agent $is_mobile {
        default 0;
        ~*(Android|iPhone|iPad|Mobile) 1;
    }
}

它的作用:

text 复制代码
输入:
$http_user_agent

        ↓ map

输出:
$is_mobile

结果:

text 复制代码
PC     → $is_mobile = 0
Mobile → $is_mobile = 1

然后:

nginx 复制代码
server {
    server_name www.test.com;

    location / {
        if ($is_mobile = 1) {
            return 302 https://m.test.com$request_uri;
        }

        root /home/html/pc;
    }
}

同一域名,不同设备读取不同目录

nginx 复制代码
map $http_user_agent $site_root {
    default /home/html/pc;
    ~*(Android|iPhone|iPad|Mobile) /home/html/mobile;
}

然后:

nginx 复制代码
location / {
    root $site_root;
    index index.html;
    try_files $uri $uri/ /index.html;
}

效果:

text 复制代码
PC
 ↓
/home/html/pc

Mobile
 ↓
/home/html/mobile

URL 可以不变化。


27. Nginx 中的简单 if

例如:

nginx 复制代码
if ($is_mobile = 1) {
    return 302 /mobile/;
}

这种简单:

text 复制代码
if + return

通常比较清晰。

不要把 Nginx 的 if 当 JavaScript 业务代码使用。

复杂逻辑建议按照职责选择:

text 复制代码
URL 匹配
→ location

条件映射
→ map

固定跳转
→ return

URL 规则修改
→ rewrite

文件存在判断
→ try_files

28. 常用判断符号

写法 含义
= 相等
!= 不相等
~ 正则匹配,区分大小写
~* 正则匹配,不区分大小写
!~ 正则不匹配
!~* 正则不匹配,不区分大小写

例如:

nginx 复制代码
if ($host = "www.test.com") {
}
nginx 复制代码
if ($http_user_agent ~* "iphone|android") {
}

29. 常用 Nginx 变量

29.1 URL 参数

请求:

text 复制代码
/test?type=mobile&id=100

Nginx:

nginx 复制代码
$arg_type

值:

text 复制代码
mobile
nginx 复制代码
$arg_id

值:

text 复制代码
100

示例:

nginx 复制代码
if ($arg_type = "mobile") {
    return 302 /mobile/;
}

29.2 请求头

客户端:

http 复制代码
X-App-Type: mobile

Nginx:

nginx 复制代码
$http_x_app_type

例如:

nginx 复制代码
if ($http_x_app_type = "mobile") {
    return 302 /mobile/;
}

规律:

text 复制代码
X-App-Type
↓
$http_x_app_type

Authorization
↓
$http_authorization

29.3 请求方法

nginx 复制代码
$request_method

可能是:

text 复制代码
GET
POST
PUT
DELETE
OPTIONS

例如:

nginx 复制代码
if ($request_method = POST) {
    return 403;
}

29.4 常用变量速查

变量 含义
$uri 当前规范化 URI
$request_uri 原始请求 URI,通常包含 Query String
$host 当前 Host
$scheme http / https
$remote_addr 客户端 IP
$request_method GET / POST / PUT 等
$http_user_agent User-Agent
$http_referer Referer
$arg_id URL 参数 id
$http_authorization Authorization Header

例如:

text 复制代码
https://www.test.com/user?id=100

大致:

text 复制代码
$request_uri → /user?id=100
$uri         → /user
$arg_id      → 100
$host        → www.test.com

30. HTTPS 基础配置

示例:

nginx 复制代码
server {
    listen 443 ssl;
    server_name www.test.com;

    ssl_certificate     /etc/nginx/cert/www.test.com.pem;
    ssl_certificate_key /etc/nginx/cert/www.test.com.key;
}

HTTP 跳 HTTPS:

nginx 复制代码
server {
    listen 80;
    server_name www.test.com;

    return 301 https://$host$request_uri;
}

访问:

text 复制代码
http://www.test.com/oa/user

跳到:

text 复制代码
https://www.test.com/oa/user

31. gzip 压缩

常用配置:

nginx 复制代码
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;

gzip_types
    text/plain
    text/css
    application/json
    application/javascript
    application/xml;

流程:

text 复制代码
JS / CSS / JSON / 文本
        ↓
      Nginx
        ↓
      gzip
        ↓
减少网络传输体积

图片、视频、zip 等很多格式本身已经压缩过,一般不需要依赖 gzip 再压一次。


32. 前端静态资源缓存

Vite 构建文件通常带 Hash:

text 复制代码
index-a31f3.js
index-f83ad.css

这类文件适合长期缓存。

例如:

nginx 复制代码
location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2?)$ {
    expires 30d;
    add_header Cache-Control "public";
}

但是:

text 复制代码
index.html

通常不建议长缓存。

原因:

text 复制代码
旧 index.html
     ↓
仍引用旧 JS Hash
     ↓
服务器已发布新版本
     ↓
旧 JS 可能已不存在
     ↓
页面白屏 / Chunk 加载失败

实际项目中通常会让 HTML 更新更及时,而带 Hash 的静态资源可以长缓存。


33. 日志与排错

访问日志:

bash 复制代码
tail -f /var/log/nginx/access.log

错误日志:

bash 复制代码
tail -f /var/log/nginx/error.log

如果遇到:

text 复制代码
403

先看:

bash 复制代码
tail -f /var/log/nginx/error.log

可能直接出现:

text 复制代码
Permission denied

这时应继续排查:

  • Linux 文件权限;
  • Nginx 运行用户;
  • SELinux;
  • 目录执行权限;
  • 文件上下文。

不要看到 403 就只改 Nginx location。


34. 三个必须掌握的 Nginx 命令

34.1 检查配置

bash 复制代码
nginx -t

正常:

text 复制代码
syntax is ok
test is successful

34.2 重新加载

bash 复制代码
systemctl reload nginx

日常建议:

bash 复制代码
nginx -t && systemctl reload nginx

比直接 restart 更适合日常改配置。


34.3 查看实际加载的完整配置

bash 复制代码
nginx -T

这是非常重要的排错命令。

如果出现:

"我明明修改了配置,为什么没有生效?"

优先执行:

bash 复制代码
nginx -T

检查:

  • server 是否真的被加载;
  • include 是否漏写;
  • 是否存在多个同域名 server;
  • location 最终配置是什么;
  • 修改的是不是实际运行的配置文件。

35. 一套固定的 Nginx 排错流程

以后遇到:

  • 403;
  • 404;
  • 502;
  • Vue 刷新 404;
  • API 404;
  • 配置不生效;

可以按照下面顺序排查:

text 复制代码
┌──────────────────────────────┐
│ 1. DNS / 域名是否指向正确服务器 │
└──────────────┬───────────────┘
               ↓
┌──────────────────────────────┐
│ 2. access.log 是否收到请求      │
└──────────────┬───────────────┘
               ↓
┌──────────────────────────────┐
│ 3. nginx -T 是否加载对应 server │
└──────────────┬───────────────┘
               ↓
┌──────────────────────────────┐
│ 4. 请求匹配哪个 location        │
└──────────────┬───────────────┘
               ↓
        ┌──────┴──────┐
        ↓             ↓
     静态文件         API
        │             │
        ↓             ↓
root + URI?       proxy_pass?
        │             │
        ↓             ↓
文件存在?       最终 URI 对吗?
        │             │
        ↓             ↓
权限/SELinux?   后端端口能 curl?
        │             │
        └──────┬──────┘
               ↓
┌──────────────────────────────┐
│ 5. Vue / Node 应用自身问题       │
└──────────────────────────────┘

常见错误与优先检查项

现象 优先检查
配置改了不生效 nginx -Tinclude
Vue 首页正常,刷新 404 try_files
JS/CSS 404 Vite baseroot + URI
API 404 proxy_pass 最后的 /
403 Linux 权限、SELinux、error.log
502 后端服务是否启动、端口是否正确
WebSocket 失败 Upgrade / Connection Header
/oa 能开但 /oa/ 异常 location 与重定向规则

36. 完整实战配置

下面给出一份适合 Vue SPA + Node API 的完整示例。

目录结构

text 复制代码
/home/html/project/
├── oa/
│   ├── index.html
│   └── assets/
│
└── crm/
    ├── index.html
    └── assets/

Vue OA

ts 复制代码
// vite.config.ts
export default defineConfig({
  base: '/oa/',
})
ts 复制代码
// router.ts
const router = createRouter({
  history: createWebHistory('/oa/'),
  routes,
})

Vue CRM

ts 复制代码
// vite.config.ts
export default defineConfig({
  base: '/crm/',
})
ts 复制代码
// router.ts
const router = createRouter({
  history: createWebHistory('/crm/'),
  routes,
})

Nginx

nginx 复制代码
server {
    listen 443 ssl;
    server_name www.test.com;

    ssl_certificate     /etc/nginx/cert/www.test.com.pem;
    ssl_certificate_key /etc/nginx/cert/www.test.com.key;

    # =========================
    # OA
    # =========================

    location = /oa {
        return 301 /oa/;
    }

    location ^~ /oa/ {
        root /home/html/project;
        index index.html;

        try_files $uri $uri/ /oa/index.html;
    }

    # =========================
    # CRM
    # =========================

    location = /crm {
        return 301 /crm/;
    }

    location ^~ /crm/ {
        root /home/html/project;
        index index.html;

        try_files $uri $uri/ /crm/index.html;
    }

    # =========================
    # API
    # =========================

    location /api/ {
        proxy_pass http://127.0.0.1:3000/;

        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 /ws/ {
        proxy_pass http://127.0.0.1:3000;

        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;
    }
}

请求关系

text 复制代码
https://www.test.com
        │
        ├── /oa/*
        │      ↓
        │   OA Vue SPA
        │
        ├── /crm/*
        │      ↓
        │   CRM Vue SPA
        │
        ├── /api/*
        │      ↓
        │   Node / Koa :3000
        │
        └── /ws/*
               ↓
            WebSocket

37. 核心知识速查表

知识点 核心理解
include 加载其他配置文件
server 根据域名、端口选择网站
location 根据 URI 分流
location = /oa 精确匹配 /oa
location /oa/ 匹配 /oa/ 下的路径
location ^~ /oa/ 前缀匹配成功后优先使用
root root + 完整 URI
alias 用真实目录替换 location URL 前缀
index 目录默认首页
try_files 尝试文件,不存在时回退
createWebHashHistory # 后路由不发送给 Nginx
createWebHistory 真实 URL,需要服务器配合
Vite base 控制静态资源基础路径
Router base 控制 Vue 页面路由基础路径
proxy_pass 反向代理到后端
return 301 永久重定向
return 302 临时重定向
rewrite 按正则修改 URL
map 根据输入变量生成新的变量
$request_uri 原始请求 URI
$uri 规范化 URI
$http_user_agent User-Agent
$arg_xxx 获取 Query 参数
$http_xxx 获取请求头
nginx -t 检查配置语法
nginx -T 查看真正加载的完整配置
reload 平滑重新加载配置
access.log 查看请求
error.log 查看错误

总结

对于前端和 Node.js 开发,学习 Nginx 最重要的不是背大量配置,而是理解请求处理链:

text 复制代码
浏览器
   ↓
server
   ↓
location
   ↓
判断怎么处理
   │
   ├── 静态资源
   │      ↓
   │   root / alias
   │      ↓
   │   try_files
   │
   ├── 后端接口
   │      ↓
   │   proxy_pass
   │
   └── 条件分流
          ↓
      return / rewrite / map

而 Vue History 模式的核心链路是:

text 复制代码
浏览器访问 /oa/user/list
          ↓
Nginx 找不到真实文件
          ↓
try_files 回退 /oa/index.html
          ↓
Vue 启动
          ↓
createWebHistory('/oa/')
          ↓
Vue Router 内部匹配 /user/list
          ↓
显示对应页面

真正理解这一条链以后,roottry_files、Vite base、Vue Router base 就不再是需要死记的配置,而是同一套 SPA 部署机制中的不同环节。


推荐后续学习

如果继续深入 Nginx,可以按照下面顺序学习:

text 复制代码
Nginx 基础
   ↓
location 完整匹配优先级
   ↓
proxy_pass URI 重写规则
   ↓
缓存策略
   ↓
跨域 CORS
   ↓
WebSocket
   ↓
限流
   ↓
负载均衡 upstream
   ↓
灰度发布

这样基本可以覆盖前端、Node.js 项目绝大多数日常部署场景。

相关推荐
考虑考虑9 小时前
cmd局部设置java变量
运维·后端·自动化运维
生活爱好者!10 小时前
哇!影视新玩法!docker部署NEOWATCH NAS
运维·docker·容器
一技安身10 小时前
【信创】银河麒麟V10、统信UOS解压7Z文件
linux·运维·服务器
迷途呀11 小时前
CI/CD 自动化部署实践:让代码提交后自动上线
linux·运维·nginx·ci/cd·自动化
闲云自留地11 小时前
动手玩 Nova:Hypervisor、主机聚合、可用分区、虚拟机生命周期实操
运维·架构·openstack
binqian12 小时前
【linux】OpenSSH升级文档
linux·运维·网络
吴声子夜歌12 小时前
Shell编程——if条件语句
linux·运维·shell
和裕12 小时前
蜂窝板 vs 七层瓦楞重型纸箱:大件工业设备运输性能与成本全对比
大数据·运维·网络·人工智能·算法
Dawn-bit12 小时前
Linux 运维基础扩展:跳板机、堡垒机与物理服务器全流程
linux·运维·服务器·云计算·运维开发