配置段(容器)

配置文件中指令的作用范围可能是整个服务器,也可能是特定的目录、文件、主机、URL。本文阐述如何使用配置段(容器)以及.htaccess文件来改变配置指令的作用范围。

配置段(容器)的类型

相关模块

  • core
  • mod_version
  • mod_proxy

相关指令

  • <Directory>
  • <DirectoryMatch>
  • <Files>
  • <FilesMatch>
  • <IfDefine>
  • <IfModule>
  • <IfVersion>
  • <Location>
  • <LocationMatch>
  • <Proxy>
  • <ProxyMatch>
  • <VirtualHost>

容器有两种基本类型。大多数容器是针对各个请求的,包含于其中的指令仅对与该容器匹配的请求起作用,而容器<IfDefine><IfModule><IfVersion>仅在启动和重新启动中起作用,如果在启动时指定的条件成立,则其中的指令对所有的请求都有效,否则将被忽略。

<IfDefine>容器中的指令只有在httpd命令行中设定了特定的参数后才有效。下例中,只有在服务器用 httpd -DClosedForNow 方式启动时,所有的请求才会被重定向到另一个站点:

<IfDefine ClosedForNow>

Redirect / http://otherserver.example.com/

</IfDefine>

<IfModule>容器很相似,但是其中的指令只有当服务器启用特定的模块时才有效(或是被静态地编译进了服务器,或是被动态装载进了服务器),注意,配置文件中该模块的装载指令LoadModule行必须在出现在此容器之前。这个容器应该仅用于你希望无论特定模块是否安装,配置文件都能正常运转的场合;而不应该用于容器中的指令在任何情况下都必须生效的场合,因为它会抑制类似模块没找到之类的有用出错信息。

下例中,MimeMagicFiles指令仅当mod_mime_magic模块启用时才有效。

<IfModule mod_mime_magic.c>

MimeMagicFile conf/magic

</IfModule>

<IfVersion>指令与<IfDefine><IfModule>很相似,但是其中的指令只有当正在执行的服务器版本与指定的版本要求相符时才有效。这个模块被设计用于测试套件、以及在一个存在多个不同httpd版本的大型网络中需要分别针对不同版本使用不同配置的情况。

  <IfVersion >= 2.1>

 # 仅在版本高于 2.1.0 的时候才生效
  </IfVersion>

<IfDefine><IfModule><IfVersion>都可以在条件前加一个"!"以实现条件的否定,而且都可以嵌套以实现更复杂的配置。

文件系统和网络空间

最常用的配置段是针对文件系统和网络空间特定位置的配置段。首先必须理解文件系统和网络空间这两个概念的区别,文件系统是指操作系统所看见的磁盘视图,比如,在Unix文件系统中,Apache会被默认安装到/usr/local/apache2 ,在Windows文件系统中,Apache会被默认安装到"C:/Program Files/Apache Group/Apache2"(注意:Apache始终用正斜杠而不是反斜杠作为路径的分隔符,即使是在Windows中)。相反,网络空间是网站被web服务器发送以及被客户在浏览器中所看到的视图。所以网络空间中的路径/dir/ 在Apache采用默认安装路径的情况下对应于Unix文件系统中的路径/usr/local/apache2/htdocs/dir/ 。由于网页可以从数据库或其他地方动态生成,因此,网络空间无须直接映射到文件系统。

文件系统容器

<Directory><Files>指令与其相应的正则表达式版本(<DirectoryMatch><FilesMatch>)一起作用于文件系统的特定部分。<Directory>配置段中的指令作用于指定的文件系统目录及其所有子目录,.htaccess文件可以达到同样的效果。下例中,/var/web/dir1 及其所有子目录被允许进行目录索引。

<Directory /var/web/dir1>

Options +Indexes

</Directory>

<Files>配置段中的指令作用于特定的文件名,而无论这个文件实际存在于哪个目录。下例中的配置指令如果出现在配置文件的主服务器段,则会拒绝对位于任何目录下的private.html的访问。

<Files private.html>

Order allow,deny

Deny from all

</Files>

<Files><Directory>段的组合可以作用于文件系统中的特定文件。下例中的配置会拒绝对 /var/web/dir1/private.html/var/web/dir1/subdir2/private.html/var/web/dir1/subdir3/private.html等任何 /var/web/dir1/ 目录下private.html的访问。

<Directory /var/web/dir1>

<Files private.html>

Order allow,deny

Deny from all

</Files>

</Directory>

网络空间容器

<Location>指令与其相应的正则表达式版本(<LocationMatch>)一起作用于网络空间的特定部分。下例中的配置会拒绝对任何以"/private"开头的URL路径的访问,比如:http://yoursite.example.com/privatehttp://yoursite.example.com/private123http://yoursite.example.com/private/dir/file.html 等所有以"/private"开头的URL路径。

<Location /private>

Order Allow,Deny

Deny from all

</Location>

<Location>指令与文件系统无关,下例演示了如何将特定的URL映射到Apache内部的处理器mod_status ,而并不要求文件系统中确实存在server-status文件。

<Location /server-status>

SetHandler server-status

</Location>

通配符和正则表达式

<Directory><Files><Location>指令可以使用与C标准库中的fnmatch类似的shell风格的通配符。"*"匹配任何字符串,"?"匹配任何单个的字符,"[seq]"匹配seq序列中的任何字符,符号"/"不被任何通配符所匹配,所以必须显式地使用。

如果需要更复杂的匹配,这些容器都有一个对应的正则版本:<DirectoryMatch><FilesMatch><LocationMatch> ,可以使用与Perl兼容的正则表达式,以提供更复杂的匹配。但是还必须注意下面配置段的合并部分关于使用正则表达式会如何作用于配置指令的内容。

下例使用非正则表达式的通配符来改变所有用户目录的配置:

<Directory /home/*/public_html>

Options Indexes

</Directory>

下例使用正则表达式一次性拒绝对多种图形文件的访问:

<FilesMatch \.(?i:gif|jpe?g|png)$>

Order allow,deny

Deny from all

</FilesMatch>

什么情况下用什么

选择使用文件系统容器还是使用网络空间容器其实很简单。当指令应该作用于文件系统时,总是用<Directory><Files>;而当指令作用于不存在于文件系统的对象时,就用<Location>,比如一个由数据库生成的网页。

绝对不要试图用<Location>去限制对文件系统中的对象的访问,因为许多不同的网络空间路径可能会映射到同一个文件系统目录,从而导致你的访问限制被突破。比如:

<Location /dir/>

Order allow,deny

Deny from all

</Location>

上述配置对http://yoursite.example.com/dir/ 请求的确起作用。但是设想在一个不区分大小写的文件系统中,这个访问限制会被http://yoursite.example.com/DIR/ 请求轻易突破。而<Directory>指令才会真正作用于对这个位置的任何形式的请求。但是有一个例外,就是Unix文件系统中的符号连接(软连接),符号连接可以使同一个目录出现在文件系统中的多个位置。<Directory>指令将不重设路径名而直接追踪符号连接,因此,对于安全要求最高的,应该用Options指令禁止对符号连接的追踪。

不要认为使用大小写敏感的文件系统就无所谓了,因为有很多方法可以将不同的网络空间路径映射到同一个文件系统路径,所以,应当尽可能使用文件系统容器。但是也有一个例外,就是把访问限制放在<Location />配置段中可以很安全地作用于除了某些特定URL以外的所有URL。

虚拟主机

<VirtualHost>容器作用于特定的虚拟主机,为同一个机器上具有不同配置的多个主机提供支持。详见虚拟主机文档

代理

<Proxy><ProxyMatch>容器中的指令仅作用于通过mod_proxy代理服务器访问的、与指定URL匹配的站点。下例中的配置会拒绝通过代理服务器访问cnn.com站点。

<Proxy http://cnn.com/*>

Order allow,deny

Deny from all

</Proxy>

允许使用哪些指令?

查阅指令的作用域,就可以知道哪些指令可以出现在哪些配置段中。从语法上看,允许在<Directory>段中使用的指令当然也可以在<DirectoryMatch><Files><FilesMatch><Location><LocationMatch><Proxy><ProxyMatch>段中使用,但也有例外:

  • AllowOverride指令只能出现在<Directory>段中。
  • Options中的FollowSymLinksSymLinksIfOwnerMatch只能出现在<Directory> 段或者.htaccess文件中。
  • Options指令不能用于<Files><FilesMatch>段。

配置段的合并

配置段会按非常特别的顺序依次生效,由于这会对配置指令的处理结果产生重大影响,因此理解它的流程非常重要。

合并的顺序是:

  1. <Directory>(除了正则表达式)和.htaccess同时处理;(如果允许的话,.htaccess的设置会覆盖<Directory>的设置)
  2. <DirectoryMatch>(和<Directory ~>)
  3. <Files><FilesMatch>同时处理
  4. <Location><LocationMatch>同时处理

除了<Directory>,每个组都按它们在配置文件中出现的顺序被依次处理,而<Directory>(上面的第1组),会按字典顺序由短到长被依次处理。例如:<Directory /var/web/dir>会先于<Directory /var/web/dir/subdir>被处理。如果有多个指向同一个目录的<Directory>段,则按它们在配置文件中的顺序被依次处理。用Include指令包含进来的配置被视为按原样插入到Include指令的位置。

位于<VirtualHost>容器中的配置段在外部对应的段处理完毕以后再处理,这样就允许虚拟主机覆盖主服务器的设置。

当请求是由mod_proxy处理的时候,<Proxy>容器将会在处理顺序中取代<Directory>容器的位置。

后面的段覆盖前面的相应的段。

技术说明

其实,在名称翻译阶段(即用AliasesDocumentRoots来映射URL到文件系统)之前,会有一个<Location>/<LocationMatch>的序列被处理,而在名称翻译结束后,这个序列的处理结果则被完全抛弃。

一些例子

这是一个演示合并顺序的例子。如果这些指令都起作用,则会按 A > B > C > D >E 的顺序依次生效。

<Location />

E

</Location>

<Files f.html>

D

</Files>

<VirtualHost *>

<Directory /a/b>

B

</Directory>

</VirtualHost>

<DirectoryMatch "^.*b$">

C

</DirectoryMatch>

<Directory /a/b>

A

</Directory>

在下面这个更具体的例子中,无论在<Directory>段中加了多少访问限制,由于<Location>段将会被最后处理,从而会允许不加限制的对服务器的访问,可见合并的顺序是很重要的,千万小心!

<Location />

Order deny,allow

Allow from all

</Location>

# 这个<Directory>段将不会实际生效

<Directory />

Order allow,deny

Allow from all

Deny from badguy.example.com

</Directory>