最近有段时间没搞项目部署了,结果在部署前端项目的时候,访问页面路由(不是根路径),nginx 响应都是 404,直接访问页面根路径,路由跳转到前端的 404 页面,排查了半天,这里再总结一下。

1. 路由访问 404 问题

前端单页应用路由分两种:哈希模式和历史模式。

哈希模式部署不会遇到啥问题,但是一般只用于本地调试,没人直接部署到生产环境。历史模式的路由跳转通过 pushStatereplaceState 实现,不会触发浏览器刷新页面,不会给服务器发送请求,且会触发 popState 事件,进而监听路由变化渲染相应页面组件,因此可以实现纯前端路由。

需要注意,使用历史模式的时候,还是有两种情况会导致浏览器发送请求给服务器:

  • 输入地址直接访问
  • 刷新页面

在这两种情况下,如果当前地址不是根路径,因为都是前端路由,服务器端根本不存在对应的文件,则会直接导致 nginx 直接响应 404。因此需要在服务器端进行配置:

server {
  listen 80;
  server_name www.example.com;
  location / {
    root /root/workspace/ruoyi-ui/dist;

    # history 模式重点就是这里
    try_files $uri $uri/ /index.html;
  }
}

try_files 的作用就是按顺序检查文件是否存在,返回第一个找到的文件。$uri 是 nginx 提供的变量,指当前请求的 URI,不包括任何参数

当请求静态资源文件的时候,命中 $uri 规则;当请求页面路由的时候,命中 /index.html 规则

至于直接访问页面根路径,会跳转到前端的 404 页面,这完全是前端路由配置问题。前端路由配置的时候,没有给根路径 / 配置规则,而对匹配不到路由的时候,配置了 404 页面,所以访问根路径会重定向到 404 页面,这个跳转是前端操作,与 nginx 无关。正常来说,前端路由配置的时候,都会给根路径 / 加一个匹配规则,例如根路径重定向到 index 路由,可以确保用户访问根路径可以正常展示页面。

2. 非根路径部署访问 404 问题

此外,在部署的时候不使用根路径,例如希望通过这样的路径去访问 /i/top.gif,如果直接修改 location 发现还会响应 404:

location /i/ {
  root /data/w3;
  try_files $uri $uri/ /index.html;
}

这是因为 root 是直接拼接 root + location,访问 /i/top.gif,实际会查找 /data/w3/i/top.gif 文件

这种情况下推荐使用 alias

location /i/ {
  alias /data/w3;
  try_files $uri $uri/ /index.html;
}

alias 是用 alias 替换 location 中的路径,访问 /i/top.gif,实际会查找 /data/w3/top.gif 文件

3. 接口请求代理

现在页面部署成功了,但是接口请求会出错,这是因为还没有对接口请求进行代理,下面配置一下:

location ^~ /prod-api/ {
	proxy_set_header Host $http_host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header REMOTE-HOST $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_pass http://192.168.31.101:8080/;
}

4. 两点补充

除了匹配请求路径访问对应文件之外,还可以配置合理的缓存机制。由于 Webpack 打包会给静态资源加上哈希值,因此可以合理配置缓存规则,提升用户体验:

  • html 文件可以配置协商缓存
  • 静态资源文件由于带有哈希,可以配置一年强缓存,提升资源二次加载速度

再注意下 location 的匹配优先级规则:

  • = 表示精确匹配。只有请求的url路径与后面的字符串完全相等时,才会命中。
  • ^~ 表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找。
  • ~ 表示该规则是使用正则定义的,区分大小写。
  • ~* 表示该规则是使用正则定义的,不区分大小写。

nginx 的匹配优先顺序按照上面的顺序进行优先匹配,而且 只要某一个匹配命中直接退出,不再进行往下的匹配

剩下的普通匹配会按照 最长匹配长度优先级来匹配,就是谁匹配的越多就用谁。

nginx 每条规则都要以分号结尾,可以运行 nginx -tc nginx.conf 查看配置规则是否生效

5. 完整的 nginx 配置

server {
  listen 80;
  server_name www.example.com;

  # html 页面访问
  location /ruoyi/ {
    # 支持 /ruoyi 子路径访问
    alias /root/workspace/ruoyi-ui/dist;

    # history 模式重点就是这里
    try_files $uri $uri/ /index.html;

    # html 文件不可设置强缓存,设置协商缓存即可
    add_header Cache-Control 'no-cache, must-revalidate, proxy-revalidate, max-age=0';
  }

  # 接口请求代理
  location ^~ /prod-api/ {
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://192.168.31.101:8080/;
  }

  # 静态资源访问
  location ~* \.(?:css(\.map)?|js(\.map)?|gif|svg|jfif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
    # 静态资源设置一年强缓存
    add_header Cache-Control 'public, max-age=31536000';
  }
}

6. 总结

单页应用历史模式路由,如果不是根路径,请求服务器都会响应 404,需要在服务器端配置 try_files 按顺序进行匹配,其中请求页面路由命中 /index.html 规则,请求静态资源命中 $uri 规则。

当页面部署在子路径下的时候,使用 root 会拼接 root + location,使用 alias 则是用 alias 替换 location 中的路径。

前端项目部署的时候,还需要配置接口请求代理。

给 html 文件配置协商缓存,由于静态资源文件会带哈希,因此可以给静态资源文件配置强缓存。

location 匹配的优先级规则注意下。

参考

前端到底用nginx来做啥

一份简单够用的 Nginx Location 配置讲解

本内容为合法授权发布,文章内容为作者独立观点,不代表开发云立场,未经允许不得转载。

CSDN开发云