nginx踩坑记和一些使用总结

前两天,有一个需求,在同一域名下,通过不同的路径去访问完全独立的项目,大概就是这样

// 访问项目一
http://abc.com
//访问项目二
http://abc.com/admin

由于之前对nginx的一些匹配理解不对,花了点时间才搞定,还是要记录一下。

两个都是vue项目,都是通过build生成dist目录,所以,只要当nginx监听到是访问http://abc.com的就把目录指向项目一,访问http://abc.com/admin就把目录指向项目二

所以开始我的配置是这样的

server {

 listen 80;
 server_name abc.com;

 location / {
   root /home/dev/项目一/dist/;
   index index.html;
  }

  location /admin {
    root /home/dev/项目二/dist/;
    index index.html;
  }
}

结果项目一访问正常,项目二nginx报404。

按照我之前的理解

1.当访问abc.com的时候,root目录指向了root /home/dev/项目一/dist/;,会在项目一的dist目录下去找index.html

2.当访问abc.com/admin的时候,root目录指向root /home/dev/项目二/dist/;,会在项目二的dist目录下去找index.html

结果项目二404,也就是说指向的目录不对。有点懵逼??

查了相关资料,发现自己之前的理解错了,项目二配置的意思是:当访问abc.com/admin的时候,把目录指向/home/dev/项目二/dist/下的admin目录,而不是dist目录!

解决方案

1.vue打包的时候,把dist目录名字改成admin

 location /admin {
    root /home/dev/项目二/;
    index index.html;
  }

2.使用nginx虚拟目录

 location /admin {
   alias /home/dev/项目二/dist/;
 }

这里还要把Vue的publicPatch和router base 改为/admin

3.使用软链接

location /admin {
  root /home/dev/项目二/
}

软链接

ln -s /home/dev/项目二/admin /home/dev/项目二/dist

这样就把访问项目下的admin变成dist


关于root目录和alias虚拟目录
  • root指定的目录是location匹配访问的path目录的上一级目录,这个path目录一定要是真实存在root指定目录下的
  • alias指定的目录是准确的,即location匹配访问的path目录下的文件直接是在alias目录下查找的

总结一下:一般原则就是,如果匹配目录是/,就配置root目录,如果匹配目录是/path,就配置alias目录

关于location的一些配置

一文弄懂Nginx的location匹配

语法规则
location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }

语法规则很简单,一个location关键字,后面跟着可选的修饰符,后面是要匹配的字符,花括号中是要执行的操作。

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

对请求的url序列化。例如,对%xx等字符进行解码,去除url中多个相连的/,解析url中的...等。这一步是匹配的前置工作。

location有两种表示形式,一种是使用前缀字符,一种是使用正则。如果是正则的话,前面有~~*修饰符。

具体的匹配过程如下:

首先先检查使用前缀字符定义的location,选择最长匹配的项并记录下来。

如果找到了精确匹配的location,也就是使用了=修饰符的location,结束查找,使用它的配置。

然后按顺序查找使用正则定义的location,如果匹配则停止查找,使用它定义的配置。

如果没有匹配的正则location,则使用前面记录的最长匹配前缀字符location。

基于以上的匹配过程,我们可以得到以下两点启示:

  1. 使用正则定义的location在配置文件中出现的顺序很重要。因为找到第一个匹配的正则后,查找就停止了,后面定义的正则就是再匹配也没有机会了。
  2. 使用精确匹配可以提高查找的速度。例如经常请求/的话,可以使用=来定义location。
示例

接下来我们以一个例子来具体说明一下匹配过程。

假如我们有下面的一段配置文件:

location = / {
    [ configuration A ]
}

location / {
    [ configuration B ]
}

location /user/ {
    [ configuration C ]
}

location ^~ /images/ {
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

请求/精准匹配A,不再往下查找。

请求/index.html匹配B。首先查找匹配的前缀字符,找到最长匹配是配置B,接着又按照顺序查找匹配的正则。结果没有找到,因此使用先前标记的最长匹配,即配置B。

请求/user/index.html匹配C。首先找到最长匹配C,由于后面没有匹配的正则,所以使用最长匹配C。
请求/user/1.jpg匹配E。首先进行前缀字符的查找,找到最长匹配项C,继续进行正则查找,找到匹配项E。因此使用E。

请求/images/1.jpg匹配D。首先进行前缀字符的查找,找到最长匹配D。但是,特殊的是它使用了^~修饰符,不再进行接下来的正则的匹配查找,因此使用D。这里,如果没有前面的修饰符,其实最终的匹配是E。大家可以想一想为什么。

请求/documents/about.html匹配B。因为B表示任何以/开头的URL都匹配。在上面的配置中,只有B能满足,所以匹配B。

location @name的用法

@用来定义一个命名location。主要用于内部重定向,不能用来处理正常的请求。其用法如下:

location / {
    try_files $uri $uri/ @custom
}
location @custom {
    # ...do something
}

上例中,当尝试访问url找不到对应的文件就重定向到我们自定义的命名location(此处为custom)。

值得注意的是,命名location中不能再嵌套其它的命名location

URL尾部的/需不需要

关于URL尾部的/有三点也需要说明一下。第一点与location配置有关,其他两点无关。

  1. location中的字符有没有/都没有影响。也就是说/user//user是一样的。
  2. 如果URL结构是https://domain.com/的形式,尾部有没有/都不会造成重定向。因为浏览器在发起请求的时候,默认加上了/。虽然很多浏览器在地址栏里也不会显示/。这一点,可以访问baidu验证一下。
  3. 如果URL的结构是https://domain.com/some-dir/。尾部如果缺少/将导致重定向。因为根据约定,URL尾部的/表示目录,没有/表示文件。所以访问/some-dir/时,服务器会自动去该目录下找对应的默认文件。如果访问/some-dir的话,服务器会先去找some-dir文件,找不到的话会将some-dir当成目录,重定向到/some-dir/,去该目录下找默认文件。可以去测试一下你的网站是不是这样的。
总结

location的配置有两种形式,前缀字符和正则。查找匹配的时候,先查找前缀字符,选择最长匹配项,再查找正则。正则的优先级高于前缀字符。

正则的查找是按照在配置文件中的顺序进行的。因此正则的顺序很重要,建议越精细的放的越靠前。

使用=精准匹配可以加快查找的顺序,如果根域名经常被访问的话建议使用=

标签: nginx

添加新评论