NginxDirectiveExecOrderTutorialCn06

= Nginx 配置指令的执行顺序（六） =

前面我们在 （五） 中提到，在一个  中使用   阶段指令时，通常情况下就是对应的 Nginx 模块注册该   中的“内容处理程序”. 那么当一个  中未使用任何   阶段的指令，即没有模块注册“内容处理程序”时，  阶段会发生什么事情呢？谁又来担负起生成内容和输出响应的重担呢？答案就是那些把当前请求的 URI 映射到文件系统的静态资源服务模块. 当存在“内容处理程序”时，这些静态资源服务模块并不会起作用；反之，请求的处理权就会自动落到这些模块上.

Nginx 一般会在  阶段安排三个这样的静态资源服务模块（除非你的 Nginx 在构造时显式禁用了这三个模块中的一个或者多个，又或者启用了这种类型的其他模块）. 按照它们在  阶段的运行顺序，依次是 ngx_index 模块，ngx_autoindex 模块，以及   模块. 下面就来逐一介绍一下这三个模块.

ngx_index 和 ngx_autoindex 模块都只会作用于那些 URI 以  结尾的请求，例如请求  ，而对于不以   结尾的请求则会直接忽略，同时把处理权移交给   阶段的下一个模块. 而  模块则刚好相反，直接忽略那些 URI 以   结尾的请求.

ngx_index 模块主要用于在文件系统目录中自动查找指定的首页文件，类似  和   这样的，例如：

location / { root /var/www/; index index.htm index.html; }

这样，当用户请求  地址时，Nginx 就会自动在 root 配置指令指定的文件系统目录下依次寻找   和   这两个文件. 如果  文件存在，则直接发起“内部跳转”到   这个新的地址；而如果   文件不存在，则继续检查   是否存在. 如果存在，同样发起“内部跳转”到 ；如果   文件仍然不存在，则放弃处理权给   阶段的下一个模块.

我们前面已经在 Nginx 变量漫谈（二） 中提到， echo_exec 指令和 rewrite 指令可以发起“内部跳转”. 这种跳转会自动修改当前请求的 URI，并且重新匹配与之对应的  配置块，再重新执行  、 、  等处理阶段. 因为是“内部跳转”，所以有别于 HTTP 协议中定义的基于 302 和 301 响应的“外部跳转”，最终用户的浏览器的地址栏也不会发生变化，依然是原来的 URI 位置. 而 ngx_index 模块一旦找到了 index 指令中列举的文件之后，就会发起这样的“内部跳转”，仿佛用户是直接请求的这个文件所对应的 URI 一样.

为了进一步确认 ngx_index 模块在找到文件时的“内部跳转”行为，我们不妨设计下面这个小例子：

location / { root /var/www/; index index.html; }

location /index.html { set $a 32; echo "a = $a"; }

此时我们在本机的  目录下创建一个空白的   文件，并确保该文件的权限设置对于运行 Nginx worker 进程的帐户可读. 然后我们来请求一下根位置（ ）：

$ curl 'http://localhost:8080/' a = 32

这里发生了什么？为什么输出不是  文件的内容（即空白）？首先对于用户的原始请求  ，Nginx 匹配出   来处理它，然后   阶段的 ngx_index 模块在   下找到了  ，于是立即发起一个到   位置的“内部跳转”.

到这里，相信大家都不会有问题. 接下来有趣的事情发生了！在重新为  这个新位置匹配   配置块时，  的优先级要高于  ，因为   块按照 URI 前缀来匹配时遵循所谓的“最长子串匹配语义”. 这样，在进入  配置块之后，又重新开始执行   、 、以及   等阶段. 最终输出  自然也就在情理之中了.

我们接着研究上面这个例子. 如果此时把  文件删除，再访问   又会发生什么事情呢？答案是返回   出错页. 为什么呢？因为 ngx_index 模块找不到 index 指令指定的文件（在这里就是 ），接着把处理权转给   阶段的后续模块，而后续的模块也都无法处理这个请求，于是 Nginx 只好放弃，输出了错误页，并且在 Nginx 错误日志中留下了类似这一行信息：

[error] 28789#0: *1 directory index of "/var/www/" is forbidden

所谓  便是生成“目录索引”的意思，典型的方式就是生成一个网页，上面列举出   目录下的所有文件和子目录. 而运行在 ngx_index 模块之后的 ngx_autoindex 模块就可以用于自动生成这样的“目录索引”网页. 我们来把上例修改一下：

location / { root /var/www/; index index.html; autoindex on; }

此时仍然保持文件系统中的  文件不存在. 我们再访问  位置时，就会得到一张漂亮的网页：

$ curl 'http://localhost:8080/' Index of / Index of / ../ cgi-bin/ 08-Mar-2010 19:36   - error/     08-Mar-2010 19:36   - htdocs/   05-Apr-2010 03:55   - icons/     08-Mar-2010 19:36   -

生成的 HTML 源码显示，我本机的  目录下还有 ,  ,  , 以及   这几个子目录. 在你的系统中尝试上面的例子，输出很可能会不太一样.

值得一提的是，当你的文件系统中存在  时，优先运行的 ngx_index 模块就会发起“内部跳转”，根本轮不到 ngx_autoindex 执行. 感兴趣的读者可以自己测试一下.

在  阶段默认“垫底”的最后一个模块便是极为常用的   模块. 这个模块主要实现服务静态文件的功能. 比方说，一个网站的静态资源，包括静态  文件、静态   文件、静态   文件、以及静态图片文件等等，全部可以通过这个模块对外服务. 前面介绍的 ngx_index 模块虽然可以在指定的首页文件存在时发起“内部跳转”，但真正把相应的首页文件服务出去（即把该文件的内容作为响应体数据输出，并设置相应的响应头），还是得靠这个  模块来完成.