问题

最近项目中遇到了一个nginx方面的问题,大概是这样的:

  • 目前项目上使用了强缓存策略,该策略的配置规则如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
location ~* \.(zip|apk)$ {
add_header Content-Disposition 'attachmet;';
}

location ~*.*\.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm)$ {
expires max;
}

location ~ .*\.(?:js|css)$ {
expires max;
}

location ~ .*\.(?:htm|html)$ {
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
}

即强制html文件不做缓存,然后所有的资源文件全部设置成最大缓存,这样只需要打包构建时,构建的文件是根据hash规则生成的,那么就能保证,浏览器请求资源文件,只会请求一次,没有额外的网络开销(资源变更,hash值变化,对浏览器来说是新的资源文件)。

  • 由于需求原因,需要引入一个网盘管理系统,结果导致location规则冲突:

因为网盘系统是一个独立系统,我们只是在登录页面做了一个简单的自动登录逻辑,然后就可以使用iframe嵌套到我们系统,最终效果如下:

但是,因为该系统使用的是cookie验证的方式,单纯的iframe嵌套会导致,cookie无法传递,进而系统登录失败,因此将嵌套地址改成了/resource-service/home.html,然后使用nginx反向代理到真正的服务器地址,这样就能做到同源了,具体配置如下:

1
2
3
location /resource-service {
proxy_pass http://127.0.0.1:30180;
}
  • 使用该策略之后,访问该路径,浏览器一直返回404,删掉其它匹配规则,则可以正常访问,所以肯定是location匹配规则冲突了,那么问题就来了,location的匹配规则是怎样的?

详解

上面的问题,我们先放下,了解了locatin的匹配规则之后,其实答案不言而喻

location语法

引用自:https://blog.csdn.net/luoyang_java/article/details/83507193

  • =:精确匹配

  • ^~:表示已某个uri路径开头,即匹配url,这里需要注意的是,nginx不对url做编码,所以匹配/static/20%/images,会被^~ /static/images匹配到(空格被忽略了)

  • ~:表示区分大小写的正则匹配

  • ~*:开头表示不区分大小写的正则匹配

  • !~:区分大小写不匹配

  • !~*:不区分大小写不匹配

  • /:通用匹配,任何请求都会匹配到

精准匹配和一般匹配

  • location = /resouce-service/location /resource-service/

    请求http://127.0.0.1/resource-service/的时候,会匹配到前者,后者不会被命中

  • location = /resouce-servicelocation /resource-service

    请求http://127.0.0.1/resource-service/的时候,会匹配到后者,前者不会被命中

  • location = resouce-servicelocation resource-service

    请求http://127.0.0.1/resource-service/的时候,会匹配到前者,后者不会被命中

  • location = resouce-servicelocation /resource-service/

    请求http://127.0.0.1/resource-service/的时候,会匹配到后者,前者不会被命中

所以:路径相同时的精准匹配优先, 必须满足/uri/或者uri,要么两边都加/,要么两边都不加/

一般匹配

location /resouce-service/location /resource-service/images

请求http://127.0.0.1/resource-service/images/test.png时,即匹配到/resouce-service/也能匹配到/resouce-service/images时,按照最长uri匹配优先,所以匹配的是后者

^~开头的规则

location ^~ /resouce-service/location /resource-service/images 和 和 location ~ /resource-service/.*\.(S:png|jpg|gif)

加上^~前缀后,会停止正则匹配,但是会继续匹配一般匹配

所以上面匹配到location ^~ /resouce-service/后会继续匹配location /resource-service/images,但是location ~ /resource-service/*\.(S:png|jpg|gif)不会匹配

精准匹配和正则匹配

location /resouce-service/location /resource-service/.*\.html$

如果http://127.0.0.1/resource-service/精准匹配到前者,所有不会继续匹配后者
如果http://127.0.0.1/resource-service/home.html会匹配前者,但不是精准匹配,所以会继续匹配后者

正则匹配

location \.html$location ~ ^/resouce-service/.*\.html$

会按照编辑顺序,顺序匹配,如上面,会优先匹配前者

@开头的uri

1
2
3
location @resouce-service {
proxy_pass https://www.yangxl.cn
}

@开头的uri,如果nginx存在对应页面,则不做反应,如果nginx不存在对应页面,则执行代理,代理到yangxl.cn上

总结

  1. 先精准命中,如果命中,立刻返回结果并结束

  2. 普通命中,如果命中多个,则按照最长命中解析

  3. 如果^~开头命中,则不会继续搜索正则命中,但会继续搜索普通命中

  4. 上面都没有命中,则按照正则规则的顺序查找,找到后返回结果并结束解析

解决

通过上面的解析,我们来分析遇到的问题,很明显请求http://127.0.0.1/resource-service/home.htmllocation /resource-service属于普通命中,会继续执行正则匹配,结果又命中了location ~ .*\.(?:htm|html)$,导致nginx解析错误

那么,解决方案就可以使用总结中的第三条了,即使用^~规避正则命中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
location / {
try_files $uri/ /index.html;
}

location ^~ /resource-service/ {
proxy_pass http://127.0.0.1:30180;
}

location ~* \.(zip|apk)$ {
add_header Content-Disposition 'attachmet;';
}

location ~*.*\.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm)$ {
expires max;
}

location ~ .*\.(?:js|css)$ {
expires max;
}

location ~ .*\.(?:htm|html)$ {
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
}