翻译自NGINX PRIMER
Nginx是个相当简单的HTTP服务器,但是在使用这个奇迹般的产品之前你还是得需要明白几点,其中最重要一点的就是Nginx首先是个反向代理,次之才是HTTP服务器,在Nginx中不必有文件的概念,这将改变我们看待和处理它的配置的方式。
首先,你要知道,nginx配置文件内容是多层级继承的,低层级会继承高层级指定的指令作为其默认值并可以进行覆盖,所以只要可能我们希望将指令写在尽可能高的层级上。
层级(hierachis)也说成是块(blocks),http块,server块,和location块的层级从高到低顺序:http->server->location。(更详细内容参见UNDERSTANDING THE NGINX CONFIGURATION INHERITANCE MODEL)
此外还有两个特殊的位置,event块和根位置(即event块和http块之上的位置),这两个地方只包含了少量的指令,你的大多数时间应该放到前面所说的那三个块上。完整的配置例子
块(blocks)有着各种各样的语义,server块就相当于Apache中的虚拟主机,location块通常对应于URI映射。
在使用官方维基时,关键字context指的是指令所在的块,如前所述,推荐你把指令写在尽可能高的层级。
##虚拟主机##
最有趣的指令莫过于server_name和root,前者指示nginx在请求的HOST头与server_name的值匹配时,使用这个server块,后者定义寻找文件的根目录。
一个例子:
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;
}
例子中,有两个虚拟主机,当浏览器所发的HOST头是不含www的domain.com或其子域名时,第一个虚拟主机命中,这是因为nginx总是选择最贴近的匹配,如果访问www.domain.com,会与第二个server块中的server_name精确匹配,第二个虚拟主机命中。
这也意味着,你可以创建一个默认的虚拟主机,来捕获所有没有合适匹配的域名。幸运的是,只需在listen指令后添加default_server标志就可实现,这样的话,所有没有HOST头或没有其他虚拟主机匹配的请求都将会发到这个默认到虚拟主机上,直接通过IP访问或访问你IP上一个随机域名就是例子。你将会在许多指导中看到server_name _;,这没有什么特殊含义,它只是一个伪造的无效HOST头以保证不会有匹配,你可以简单地省略掉server_name达到同样效果。
server {
listen 80 default_server;
index index.html;
root /var/www/default;
}
##Locations## 如果你是从Apache阵营过来的,那么接下来你需特别注意。nginx通常不是用复杂的rewrites,而是用location块实现同样功能。
最需要注意的是,所有locations(named locations除外)都工作不带查询参数的URI上,而且只有一个location块被运行。这也是为什么我推荐把指令放在尽可能高层级的块中,在location / 中定义到root指令不会在location /images中有效,除非定义在server块中。相信你会明白,把指令放在尽可能高层级到块中会避免代码重复,也会减少很多头疼事儿。
还有重要的一点是,正如server_name一样,nginx也总是选择最贴近的location块。详细规则可参见location directive wiki entry
下面再看下使用location块的几个例子,在例子中我们在URI /forum/ 上运营着一个论坛,最近这个论坛迁移到一个子域名下。我们需要将旧URL重定向到新URL上,为实现它,我们使用一个带有命名变量的regex location,然后发布。
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中的文件的请求由/home/domain.com 根目录进行处理。
##处理PHP##
PHP或着其他任何后端,都可以与locations关系良好,也就是说,我们可以定义一个location块来捕获所有PHP文件请求。
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;
fastcgi_pass 127.0.0.1:9000;
}
}
像之前提到的,nginx不关心files而是关心locations,这也是为什么我在php块中使用try_files指令。这个location块匹配所有后缀为.php的URI,但它不关心这个URI对应的是不是一个文件。因此 请求/forum/avatars/user2.jpg/index.php 会与这个块匹配,然后发向PHP,如果PHP没有合理配置,将会在/forum/avatars/user2.jpg/index.php不存在时执行/forum/avatars/user2.jpg,这产生了巨大的安全风险。注意这不是nginx中的bug,这是刻意的行为,不会被fixed。
这可以通过在PHP端在php.ini中设置cgi.fix_pathinfo=0进行修正。
最终结果是,如果.php文件存在,会通过fastcgi传递给运行在9000端口的PHP进行处理。
##SEF URLs##
上面的设置可以工作了,但是现在最狂热的是为了SEO,需要有搜索引擎友好的URLs。通常这涉及到一些rewrites,但是在nginx中,我们只需一行代码,只要后端脚本书写正常。
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;
fastcgi_pass 127.0.0.1:9000;
}
}
注意到区别了吗?try_files行意思是,它首先试着访问完整的URL,静态文件请求就到此为止了;然后尝试访问完整到URL+斜杠,即查找目录;最后,如果上面都不奏效,它将会发起一个/index.php请求,这会执行全新的匹配,当然会命中第二个location块,fastcgi_pass这个请求,PHP将会在$_SERVER['PATH_INFO']中存放完整的URI。简单、优雅、容易理解。
##调试##
Nginx有时是个复杂到服务器,幸好,我们有错误日志帮助我们发现哪里出了错。如果你查看error log directive in the wiki,你会发现它带有第二个参数,你可以定义nginx输出多少信息。信息的价值就是可以提供足够的信息调试大部分问题。
##继续阅读##
official nginx wiki是个很有价值的资源,扩充nginx知识的一个很好途径是读在Core module中的指令和变量。完整的配置例子会让你对更复杂的配置文件有个认识。