Nginx root 和alias
作者:人世间
链接:https://www.jianshu.com/p/4be0d5882ec5
静态文件
基本配置
基本配置如下:
/etc/nginx/sites-enabled/pro.conf
1 | server { |
项目的目录如下:
1 | pro tree. |
分别有两个静态文件夹,一个是static
,另外一个是upload
。
初识root
root
是指定项目的根目录,适用与server
和location
。可以指定多个,如果locaiton
没有指定,会往其外层的server
或http
中寻找继承。
访问http://192.168.33.10/static/stc.jpg
会发现图片已经返回。
我们还尚未配置 location
,为啥会正确的找到文件?
学习root
或者alias
指令的时候,最好的办法是给文件拓展名加上一个字符,使得该文件在硬盘中不存在,那么就能从nginx
的error.log
中看到nginx
寻找文件的方式。
访问 http://192.168.33.10/static/stc.jpgx
,然后查看 /var/log/nginx/pro/error.log
文件,可以看到如下的错误信息:
1 | 2016/09/28 07:41:48 [error] 4416#0: *70 open() "/vagrant/pro/static/stc.jpgx" failed (2: No such file or directory), client: 192.168.33.1, server: localhost, request: "GET /static/stc.jpgx HTTP/1.1", host: "192.168.33.10" |
即/vagrant/pro/static/stc.jpgx
文件不存在。
的确我们没有这个文件。如果文件名正确,就能访问,原因是由于在server
中指定了root /vagrant/pro
,此时的nginx
就在该目录下寻找文件,而url
上的地址,正好和文件的路径一致
1 | http://192.168.33.10 /static/stc.jpg |
由此可以猜想,nginx
中root
指令的地址,其实是替换了匹配后的url
中的host
。
root指令
为了验证上面的猜想,需要多写几个location做实验。添加一个location配置如下:
1 | location ^~ /static { |
再次访问k,发现并不能显示图片了,查看error.log
返回如下:
1 | 2016/09/28 07:48:57 [error] 5978#0: *71 open() "/vagrant/pro/static/static/stc.jpg" failed (2: No such file or directory), client: 192.168.33.1, server: localhost, request: "GET /static/stc.jpg HTTP/1.1", host: "192.168.33.10" |
nginx
把地址识别成/vargrant/pro/static/static/stc.jpg
多了一个static
,套用上面的规则,其组合为192.168.33.10 == /vagrant/pro/static
,url
是/static/stc.jpg
。置换可以得到/vagrant/pro/static + /static/stc.jpg
。与错误的error
一致。解决方案就是把root
中的static
去掉,马上就能访问图片了。
既然是那么把文件夹static
命名为stc
,其结果又会怎样?
1 | location ^~ /static { |
访问 http://192.168.33.10/static/stc.jpg
得到错误:
1 | 2016/09/28 07:54:46 [error] 5992#0: *73 open() "/vagrant/pro/static/stc.jpg" failed (2: No such file or directory), client: 192.168.33.1, server: localhost, request: "GET /static/stc.jpg HTTP/1.1", host: "192.168.33.10" |
计算路径/vagrant/pro + /static/stc.jpg
, 找不到/vagrant/pro/static/stc.jpg
文件,符合之前所说的规则,尝试修改location
:
1 | location ^~ /stc { |
因为url
变了,访问http://192.168.33.10/stc/stc.jpg
,才能够找到图片。现在把stc
文件夹变回static
。
root 与 斜杠
很多人会疑惑,路径最后的斜杠/是否要加呢?location
中的static
后面的斜杠,和匹配后的url
有关,不再赘述。root
中的路径的斜杠/可以再通过实验确定。把location
配置如下:
1 | location ^~ /static/ { |
访问http://192.168.33.10/static/stc.jpg
一切正常,访问http://192.168.33.10/static/stc.jpg
,error
为找不到”/vagrant/pro/static/stc.jpgs
“文件。
如果按照root替换host的规则,那么替换过程为/vagrant/pro/ + /static/stc.jpg == /vagrant/pro//static/stc.jpg
。在*nix系统中, 多个斜杠和一个斜杠是等价的,也就是/vagrant/pro//static/stc.jpg
与/vagrant/pro/static/stc.jpg
一样。
这样一来,root
路径后面的斜杠,加与不加效果都一样。既然如此,肯定有人会想到这么配置:
1 | location ^~ static/ { |
如果安装之前上面的即算法,那么应该是/vagrant/pro + static/stc.jpg
,相加的应该是/vagrant/prostatic/stc.jpg
,按理说应该是错误,可是实际上却能访问图片。咄咄怪事?
如果对前文nginx location
的url
匹配规则了解的话,应该看出来了其实 ^~ static/
并不能匹配。
修改 location
1 | location ^~ static/ { |
访问http://192.168.33.10/static/stc.jpg
依然可以得到图片,没有跳转google,说明并没有匹配^~ static/
。
其实原理也很简单,还记得我们第一次实验,当时尚未配置location
,也同样可以返回图片。没错,尽管^~ static/
没有匹配,而外层的server
定义了root
为/vagrant/pro
,因此搜索图片正常返回,再注释外层的root
,再一次访问。此时会得到一个404
,查看error
如下:
1 | 2016/09/28 08:18:15 [error] 6227#0: *82 open() "/usr/share/nginx/html/static/stc.jpg" failed (2: No such file or directory), client: 192.168.33.1, server: localhost, request: "GET /static/stc.jpg HTTP/1.1", host: "192.168.33.10" |
/usr/share/nginx/html/static/stc.jpg
,说明即使没有指定root
,nginx
默认也有一个root
,/usr/share/nginx/html
。当然,这个配置和 ^~ static/
没有关系。
如果~ static/stc.jpgs
? 那么就能命中,此时访问图片,依然能够正确的解析,因此,并不存在/vagrant/pro + static/stc.jpg
这种情况。理解这里的关键是 root
替换host
,并加上匹配后的url
,匹配后的url
当然包括前面的斜杠,匹配部分的url
则不会。
对于 ~ static/stc.jpgs
?模式,访问urlhttp://192.168.33.10/static/stc.jpg
1 | 匹配后的url为 /static/stc.jpg |
掌握这个很重要,直接关系到后面alias
指令与斜杠的关系。
对于root
指令,我们可以归纳。
1、对于匹配后的url
地址,将匹配的location
中的root
路径替换访问url
的host
即得到文件的真实地址。(多个斜杠其实等价于一个斜杠)
2、如果不匹配location
,则寻找更外层的root
做替换。
3、root
指令最后的斜杠可加可不加。
alias指令
对于root
,操作上很简单,只要把root
地址替换host
后就是文件在硬盘路径(真实地址)。对于alise
,它并不是替换匹配后的url
地址,而是替换匹配部分的url
。alias
指令也可以有多个。
添加一个location
,和root
的方式几乎一样:
1 | location ^~ /upload { |
访问http://192.168.33.10/upload/up.png
并没有图片,查看error
得到:
1 | 2016/09/28 08:36:18 [error] 6312#0: *90 open() "/vagrant/pro/up.png" failed (2: No such file or directory), client: 192.168.33.1, server: localhost, request: "GET /upload/up.png HTTP/1.1", host: "192.168.33.10" |
可见 alias
的模式并不是/vagrant/pro + /upload/up.png
,而是 /vagrant/pro + /up.png
。
alias
这个词在计算机里很常用,字面意思是“别名”,顾名思议就是换一个名字啦。实际替换规则就是把匹配的url
地址,换成alias
中的路径即可。例如上述的例子替换过程可以模拟如下:
1 | 过程 |
为了修改图片的访问,修改locaton
如下:
1 | location ^~ /upload { |
此时访问http://192.168.33.10/upload/up.png
就能得到正确的图片啦,仿造上面的计算过程为:
1 | 过程 |
从结果可以看出,正确的找到了文件路径,如果alias
指令路径加上斜杠,那么计算处理的文件路径为:
1 | /upload == /vagrant/pro/upload |
多个斜杠是合法的。等价于一个斜杠的情况。
下面修改locaiton
如下:
1 | location ^~ /upload/ { |
此时匹配时的url则变成 /upload/ + up.jpg
, 那么置换的结果为 /vagrant/pro/upload + up.png
,而/vagrant/pro/uploadup.png
的路径是非法的,从error
中也能看到置换的错误:
1 | 2016/09/28 08:52:44 [error] 6452#0: *92 open() "/vagrant/pro/uploadup.png" failed (2: No such file or directory), client: 192.168.33.1, server: localhost, request: "GET /upload/up.png HTTP/1.1", host: "192.168.33.10" |
解决办法也很简单,把/vagrant/pro/upload
改成 /vagrant/pro/upload/
即可。由此可见,alias
最后的斜杠并不像root
指令那样可有可无,是否需要,取决于配合loacation
的url
匹配模式。
前文root
模式中,考虑了没有根的斜杠(~ static/stc.jpgs?)
这种情况,alias
情况下会很难捕捉错误。如果locaion
配置如下:
1 | location ^~ upload/ { |
替换置换的文件路径应该为 /vagrant/pro/upload/up.png
,可是实际测试中,这样配置alias
,会一直导致一个301的重定向,如果alias
目录没有打开autoindex
,则会抛出一个403错误。具体情况尚未知晓,不知道是不是nginx
的bug
。为了避免这种情况,使用alias
的时候,尽量不要配置location
为 ^~ upload/
的模式,并且不从根指定url
,还是显得不伦不类。alise
作为别名,比起root
的一大好处就是不一定要url
上的路径和文件路径一样,因为alise
并不是替换host
,而是替换匹配部分的host
。修改配置如下:
1 | location ^~ /upload/ { |
访问http://192.168.33.10/upload/stc.jpg
或者 http://192.168.33.10/upload/flask/m.png
都能正确的访问到static
目录下的文件,尽管url
上是upload
。
替换规则也很简单,/upload/ == /vagrant/pro/static/
得到 /vagrant/pro/static/ + stc.jpg
或/vagrant/pro/static/ + flask/m.png
。
总结nginx
的静态文件配置中,root
和alias
指令都能实现。为了避免混淆,尽量不要写没有根路径的url
模式,即避免 static/
这样的开头,根路径的斜杠需要保留,没有根路径其实也很奇怪。
root
和alias
的区别主要在于替换的部分,root
模式中,会把root
配置的路径替换匹配后的url
中的host
。alias
则把他指定的路径,替换url
中匹配的部分。指令中的斜杠对于root
指令没有影响,对于alise
则按照替换规则匹配即可。
1 | root 指令 |
了解了root
和alise
之后,通常最佳实际是配置一个项目的根root
,其他的文件夹则使用alias
,毕竟alias
更加灵活。