Nginx是一个快速发展中的web服务器。2013年7月,nginx成为排名前1000的流量大站中使用最广的的web服务器。 以至于学习nginx变成一个必要的事情。然而,nginx提供的官方文档更多讲述的是其API,而不是nginx实际上如何去工作的。 为了弥补这一点,本文旨将带你快速通篇了解一下nginx配置文件中最重要的部分。
Nginx实际上是一个相当简单的HTTP服务器,但是,由于我们大多数人原先是Apache用户, 所以在我们开始使用之前,需要留意其与nginx配置的不同。开始学习Nginx,首先要弄清楚的是, 其将反向代理摆在首位,然后才是HTTP服务器。它更多的关注的是Urls,而不是文件怎么被提供服务。这一点决定了我们将如何去配置它。
Nginx的配置文件是一个继承的层次结构,我们在较高层次块中指定的指令,会被做为更低层次块的一个默认值, 从这点上看,我们应该尽量在更高层级去配置一些通用的设置。既然了解到它是一个继承的关系,所以我们仍有可能在具体使用场景中去改变它。
Nginx中有3个层次结构的块通常被使用盗。他们分别是http-block、server-block 和 location-block 这三块的继承层级如下所示:http - > server - > location。
此外,还有两个特殊的位置,event-block 和 root(event-block 和 http-block包含在其中)。这两个位置只包含了少量的指令。
我们大部分时间将用于其他三个块(http、server、location)。
这些块分别具有不同的含义。server-block与Apache中被视为虚拟主机的内容相类似,而location block通常相比于Apache的URI。
所以阅读文档时,文档中context字段声明这个指令可以用于那些块,如前所述,我们建议在最顶层的块中指定该指令。
比如下面这个图,context字段表示我们可以在 http、server 和 location快去使用这个slice size;
指令,则更建议在http块中去使用这个指令。
首先,我们从这个块中最有趣的指令server_name和root开始了解server块。 前者表示nginx在与request header中HOST信息相匹配时使用此这个server块的配置,后者定义在查找文件时以root指定的位置作为开始。
一个最基本的虚拟主机配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name domain.com *.domain.com;
return 301 $scheme://www.domain.com$request_uri;
}
server {
listen 80;
server_name www.domain.com;
index index.html;
root /home/domain.com;
}
例子中我们定义了两个虚拟主机。 当domain.com或domain.com的任何子域(除了www)和浏览器request header的HOST字段匹配时, 第一个虚拟主机配置会被使用。当有多个匹配时,nginx会选择更具体的一个匹配,如果直接访问www.domain.com,则选择第二个块。
这也意味着我们可以创建一个没有明确匹配规则的默认虚拟主机用于适配所有的domains。
这样我们可以很简单的实现与将default_server标志添加到listen指令内一样的功能。listen 80 default_server;
。
以至于所有header里没有HOST字段,或者header里虽然有HOST字段,但是不与任何vhost(虚拟主机)相匹的request会被发送至这个默认虚拟主机去处理。
比如那些通过IP地址发起的请求,或者那些绑定了一个随机域名指向我们服务器IP的请求。
另外,我们常看到一些文档将server_name配置为server_name _;
,意于不需关心这个配置。
它只是一个虚假无效的HOST header,可以用来确保没有任何东西与之匹配,但是直接不定义server_name更省事。
1
2
3
4
5
6
server {
listen 80 default_server;
index index.html;
root /var/www/default;
}
如果我们是从Apache切换到nginx,那么location 块是一个值得留意的地方。 透过location块,在Nginx内我们通常不需要任何复杂的重写配置,就可以达成一样的效果。
特别需要我们去留意的是,所有与那些没有任何参数附着的URI都匹配的location,最终只有其中一个被应用。
这也是我们建议将指令放在最顶层的原因。比如定义在location /
块里面的一个root指令,对location /images
块来说是不可用的,
除非我们把root指令定义在了server块。你应该能了解到将指令定义在最上面的块中能有效防止代码重复和减轻我们的头痛症。
另一个和location块相关的要点是,与server_name指令一样,nginx使用最具体匹配的location块。 关于各种location的设置的一些规则,location指令的官方文档条目已经解释的足够清楚了,可以先去阅读了解。
关于如何使用location块,我们先来看一些具体示例。
在这个例子中,我们假设有一个论坛运行在 /forum/
这个URI上面,并且最近将它移动到了一个子域名下面。
我们需要将指向旧URL的访问重定向到新的URL。为此,我们使用一个有命名变量的正则表达式表示的location,在其中发出重定向。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80 default;
server_name www.domain.com;
root /home/domain.com;
# This will match any URI beginning with /forum
location ~ ^/forum/(?P.*)$ {
return 301 $scheme://forum.domain.com/$1;
}
}
server {
listen 80;
server_name forum.domain.com;
index index.php;
root /home/domain.com/forum;
}
现在所有指向/forum的请求已经可以成功的导向了我们的新子域名, 同时,当请求文件时,/forum目录下不存在的文件,将继续使用原来location中root指定的/home/domain.com目录
PHP —— 或者其他任何后端都可以很好的和利用location来完成工作,我们可以定义一个location来捕捉任何PHP文件的请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name forum.domain.com;
index index.php;
root /home/domain.com/forum;
location ~* \.php$ {
include fastcgi.conf # I include this in http context, it's just here to show it's required for fastcgi!
try_files $uri =404; # This is not needed if you have cgi.fix_pathinfo = 0 in php.ini (you should!)
fastcgi_pass 127.0.0.1:9000;
}
}
和前面说的一样,nginx不关心其是否是对文件的请求,而是其location路由, 这就是为什么我们有一个try_files的指令位于php 的location块里。 这个location块匹配以.php结尾的URI,但它不关心它是否在请求文件。 因此,对/forum/avatars/user2.jpg/index.php的请求将匹配并发送给PHP,如果PHP未正确配置, 则PHP将在/forum/avatars/user2.jpg/index.php不存在时,将会被路由至/forum/avatars/user2.jpg。 这潜在巨大的安全风险。请注意,这不是nginx中的bug,它是预期的行为,因此不会有“修复”的fix feature。
这一点同样可以通过在PHP的配置文件php.ini中设置cgi.fix_pathinfo=0来解决
结果是,任何对.php文件的请求,在其存在时,都将通过fastcgi传由我们运行在9000端口的PHP去处理
上面这个设置虽然有效,但是如下,现在我们所有的折腾都是出于SEO,而想要拥有对搜索引擎友好的URL。 通常这涉及相当多的重构,但是对于nginx,我们可以只使用一行,就可以使其正确运行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 80;
server_name forum.domain.com;
index index.php;
root /home/domain.com/forum;
location / {
try_files $uri $uri/ /index.php;
}
location ~* \.php$ {
include fastcgi.conf; # I include this in http context, it's just here to show it's required for fastcgi!
try_files $uri =404; # This is not needed if you have cgi.fix_pathinfo = 0 in php.ini (you should!)
fastcgi_pass 127.0.0.1:9000;
}
}
你留意到了这个变化了吗?它真的是一个很小改动。 try_files这一行表明它将首先尝试访问完整的URI,这意味着对静态文件的请求到此为止。 其次它将尝试完整的URI加斜杠,将其认为是对一个目录的请求。 最后,如果这些尝试都不成功,它会将请求发送到/index.php并尝试执行新的location匹配, 这显然会命中我们的PHP location块,并fastcgi_pass这一个请求。 PHP将在$_SERVER[‘REQUEST_URI’]取得完整的URI。Simple, elegant and easy to understand.
诚然,Nginx有时是一个挺复杂复杂的服务器,幸好我们有一个很好的错误日志可供帮助,协助我们找出出错的地方。 如果您检查文档中的error log指令,您会注意到它提供了第二个参数,这将让我们定义nginx输出的信息量。 一个合适warn值,可以为我们提供足够的信息来调试大多数问题。