基于 url 动态配置 try_file

说明

  • 前端 vue 项目构建后使用 nginx 作为静态资源的代理
  • 由于 url 内 # 的原因,项目需使用 history 模式,所以需要 nginx 配置 try_file,如:
1
2
3
4
5
6
7
server {

    location ~* ^/t_return/ {
        root /app/webroot;
        try_files $uri $uri/ /t_return/index.html ;
    }
}
  • 当项目过多时,由于 try_file 指定的各项目的 index.html,所以需要配置多条 nginx location,nginx 配置维护压力大

解决方案

  • 利用 nginx 捕获组捕获 url 中 url 的一部分,动态拼接到 try_file 内
    • nginx 的捕获组使用 () 定义,可以将括号内匹配的部分保存到一个变量中,如例子中的捕获 [^\/] 保存到 $1 中
    • 定义一级 web_project,用于区分所有的前端项目
    • 各前端子项目 web_project 下,try_file 到子项目内的 index.html
1
2
3
4
5
6
7
server {

    location ~* ^/web_project/([^\/]+)/ {
        root /app/webroot;
        try_files $uri $uri/ /web_project/$1/index.html ;
    }
}

测试

  • webroot 目录结构
1
2
3
4
5
6
$ tree
.
├── bbb
│   └── index.html
└── ccc
    └── index.html
  • 请求
1
2
3
4
$ curl http://127.0.0.1/web_project/bbb/ddddd.html
index in bbb
$ curl http://127.0.0.1/web_project/ccc/ddddd.html
index in ccc

补充

if 导致变量失效

  • 针对需要对 html 增加 no-store header 的场景,使用上述配置时,会由于 $1 变量丢失导致 try_file 失效
  • 解决方案如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
server {

    location ~* ^/web_project/([^\/]+)/ {
        # 在 if 前,将 $1 的值赋给 base_req_path,tryfile 内使用此变量
        set $base_req_path $1;

        # html/htm 类型文件增加 no-store header
        if ($request_filename ~* .*\.(?:htm|html)$)
        {
          add_header Cache-Control "no-store";
        }

        root /app/webroot;
        try_files $uri $uri/ /web_project/$base_req_path/index.html ;
    }
}