本文目錄:1.1 httpd命令和apachectl命令1.2 配置文件規則和常見指令 1.2.1 Listen指令 1.2.2 ServerRoot指令 1.2.3 DocumentRoot指令 1.2.4 DirectoryIndex指令 1.2.5 ServerName和ServerAlias ...
本文目錄:
1.1 httpd命令和apachectl命令
1.2 配置文件規則和常見指令
1.2.1 Listen指令
1.2.2 ServerRoot指令
1.2.3 DocumentRoot指令
1.2.4 DirectoryIndex指令
1.2.5 ServerName和ServerAlias
1.2.6 Include指令
1.2.7 Define和UnDefine指令
1.2.8 VirtualHost指令
1.2.9 Options和AllowOverride指令
1.2.10 Require指令
1.2.11 長連接相關指令
1.3 容器類指令
1.3.1 容器< Directory >和< Files >
1.3.2 容器< Location >
1.3.3 < IfDefine >、< IfModule >和< IfVersion >條件判斷
1.3.4 < If >、< ElseIf >和< Else >容器
1.4 配置文件的合併規則
本文主要介紹介紹的是httpd的配置文件,包括一些最基本的指令、配置規則、配置合併規則。以下指令完全來自官方手冊以及我自己的總結和整理。
1.1 httpd命令和apachectl命令
[root@xuexi ~]# httpd -h
Usage: httpd [-D name] [-d directory] [-f file]
[-C "directive"] [-c "directive"]
[-k start|restart|graceful|graceful-stop|stop]
[-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S] [-X]
Options:
-D name : 定義一個在< IfDefine name >中使用的name,以此容器中的指令
-d directory : 指定ServerRoot
-f file : 指定配置文件
-C "directive" : 指定在載入配置文件前要處理的指令(directive)
-c "directive" : 指定在載入配置文件後要處理的指令
-e level : 顯示httpd啟動時的日誌調試級別
-E file : 將啟動信息記錄到指定文件中
-v : 顯示版本號
-V : 顯示編譯配置選項
-h : 顯示幫助信息
-l : 顯示已編譯但非動態編譯的模塊,即靜態編譯的模塊
-L : 顯示靜態模塊可用的指令列表
-t -D DUMP_VHOSTS : 顯示虛擬主機的設置信息
-t -D DUMP_RUN_CFG : 顯示運行參數
-S : 等價於-t -D DUMP_VHOSTS -D DUMP_RUN_CFG。在調試如何解析配置文件時非常非常有用
-t -D DUMP_MODULES : 顯示所有已被載入的模塊,包括靜態和動態編譯的模塊
-M : 等價於-t -D DUMP_MODULES
-t : 檢查配置文件語法
-T : 不檢查DocumentRoot,直接啟動
-X : 調試模式,此模式下httpd進程依賴於終端
-k : 管理httpd進程,接受start|restart|graceful|graceful-stop|stop
apachectl命令和httpd命令基本相同。httpd接受的選項,apachectl都接受。但apachectl還可以省略"-k"選項直接管理httpd進程。
apachectl [-k] start
:按照預設路徑,讀取預設配置文件,並啟動httpd。apachectl [-k] stop
:關閉httpd進程。apachectl [-k] restart
:重啟httpd進程。apachectl [-k] graceful-stop
:graceful stop,表示讓已運行的httpd進程不再接受新請求,並給他們足夠的時間處理當前正在處理的事情,處理完成後才退出。所以在進程退出前,日誌文件暫時不會關閉,正在進行的連接暫時不會斷開。apachectl [-k] graceful
:graceful restart,即graceful-stop+start。apachectl [-k] configtest
:語法檢查。
在systemd環境下,還可以使用apacectl status
或systemctl status httpd
查看httpd進程的詳細信息。
1.2 配置文件規則和常見指令
httpd的核心體現在配置文件,各種功能都通過配置文件來實現。使用rpm包安裝的httpd預設配置文件為/etc/httpd/conf/httpd.conf。可以使用httpd -f config_path
指定要載入的配置文件。
配置文件中全是一些指令配置,每個指令都是某個模塊提供的。以下是配置文件的一些規則:
- 指令生效方式是從上往下讀取,這一點非常非常重要。很多指令的位置強烈建議不要改變,例如
Include conf.d/*.conf
指令建議不要移動位置。 - "#"開頭的行為註釋行,只能行頭註釋,不能行中註釋。
- 對大小寫不敏感,但是建議指令名稱採用"駝峰式"命名。例如ServerRoot,DocumentRoot。
- 一行寫不下的可以使用"\"續行,但是"\"後不能有任何字元,包括空格也不允許。
- 指令配置格式為"Directive value",例如"ServerRoot /etc/httpd",如果value中包含特殊字元或空格,則必須使用雙引號包圍。
- 由於可以通過Include指令包含其他配置文件,又支持各種路徑的容器,所以在httpd啟動時會先進行配置文件的合併。理解合併規則非常重要,具體見配置文件合併規則。
1.2.1 Listen指令
設置監聽套接字。設置方式很簡單,包括以下幾種情況:
# 監聽兩個埠
Listen 80
Listen 8000
# 監聽套接字綁定在給定地址和埠上
Listen 192.170.2.1:80
Listen 192.170.2.5:8000
1.2.2 ServerRoot指令
該指令設置httpd的安裝位置,也就是常稱之為的basedir,在此目錄下應該具有module、logs等目錄。rpm安裝的httpd的ServerRoot預設為/etc/httpd,編譯安裝的ServerRoot路徑由"--prefix"選項指定,例如/usr/local/apache。
[root@xuexi ~]# ls -l /usr/local/apache/
total 52
drwxr-xr-x 2 root root 4096 Sep 27 20:46 bin
drwxr-xr-x 2 root root 4096 Sep 27 20:46 build
drwxr-xr-x 2 root root 4096 Sep 27 20:46 cgi-bin
drwxr-xr-x 3 root root 4096 Sep 27 20:46 error
drwxr-xr-x 2 root root 4096 Sep 30 11:33 htdocs
drwxr-xr-x 3 root root 4096 Sep 27 20:46 icons
drwxr-xr-x 2 root root 4096 Sep 27 20:46 include
drwxr-xr-x 2 root root 4096 Sep 30 01:40 logs
drwxr-xr-x 4 root root 4096 Sep 27 20:46 man
drwxr-xr-x 14 root root 12288 Jul 7 01:38 manual
drwxr-xr-x 2 root root 4096 Sep 27 20:46 modules
這個指令很關鍵,安裝好apache後一般不會去做任何修改,因為很多指令的路徑以及相對路徑都是基於此路徑的。嚴格地說,除了網路路徑,基本上所有本地文件系統類的路徑只要不是絕對路徑,相對路徑都基於此路徑展開。
例如,當指定"ServerRoot /usr/local/apache"時,下麵幾個指令中描述的本地路徑,等號前面的採用的都是相對路徑,等號右邊的都是他們等價的絕對路徑寫法。
DocumentRoot "htdocs" = DocumentRoot "/usr/local/apache/htdocs"
LoadModule dir_module modules/mod_dir.so = LoadModule dir_module /usr/local/apache/modules/mod_dir.so
ErrorLog "logs/error_log" = ErrorLog /usr/local/apache/logs/error_log
Alias /net_path local_fs_path = Alias /net_path /usr/local/apache/local_fs_path
Include conf.d/vhost.conf = Include /usr/local/apache/conf.d/vhost.conf
但註意,容器< Directory PATH >的PATH一般設置為文件系統的絕對路徑,因為它是路徑匹配性質的。但它仍可以使用相對路徑時,此時它相對的是根文件系統的"/",而非ServerRoot。
所以,這個指令強烈不建議做任何修改,修改是很簡單,但是牽一發而動全身。
1.2.3 DocumentRoot指令
如果說,ServerRoot是httpd中本地文件相對路徑的根,那麼DocumentRoot就是網路路徑相對路徑的根。顧名思義,DocumentRoot是文檔的根目錄,這個文檔的意思是展現在網路上的文檔。使用rpm包安裝的httpd的DocumentRoot預設值為"/var/www",編譯安裝的httpd,其DocumentRoot預設為"PREFIX/htdocs",也就是"$ServerRoot/htdocs"。
設置DocumentRoot後,將需要在網路上訪問的文件都放進此目錄下即可。
例如,假設httpd所在主機IP為192.168.100.14,DocumentRoot使用預設的/usr/local/apache/htdocs,那麼下麵幾個URL中,左邊的是瀏覽器中輸入的值,右邊的是其訪問的伺服器上的資源路徑。
http://192.168.100.14/index.html ==> /usr/local/apache/htdocs/index.html
http://192.168.100.14/index.php ==> /usr/local/apache/htdocs/index.php
http://192.168.100.14/subdir/index.html ==> /usr/local/apache/htdocs/subdir/index.html
http://192.168.100.14/subdir/index.php ==> /usr/local/apache/htdocs/subdir/php
也就是說,DocumentRoot的值對應的是http://192.168.100.14/
的"/"。
1.2.4 DirectoryIndex指令
該指令設置的是"當搜索的URL中的路徑使用了"/"結尾時,httpd將搜索該指令所指定的文件響應給客戶端"。也就是說,當url表示搜索的是目錄時,將查找該目錄下的DirectoryIndex。註意,很多時候如果沒有給定尾部的"/",httpd的dir_module模塊會自行加上"/",當然,是否補齊尾隨的"/",也是可以控制的,見DirectorySlash指令。
DirectoryIndex的設置格式為:
DirectoryIndex disabled | local-url [local-url]
例如,當設置"DirectoryIndex index.html"時,如果在瀏覽器中輸入下麵左邊的幾個URL,httpd將響應右邊對應的文件。
http://192.168.100.14 ==> $DocumentRoot/index.html
http://192.168.100.14/newdir/ ==> $DocumentRoot/newdir/index.html
可以指定多個index文件,它們將按順序從左向右依次查找,並返回第一個找到的index文件。例如:
<IfModule dir_module>
DirectoryIndex index.php index.html /mydir/index.html
</IfModule>
當瀏覽器中輸入http://192.168.100.14/
時,將首先搜索index.php,如果該文件不存在,則再搜索index.html,如果還找不到,則再找該目錄的子目錄下的文件/mydir/index.html。但這不表示http://192.168.100.14/mydir/
會搜索/mydir/index.html。
可以使用多個DirectoryIndex指令進行追加設置,它等價於單行設置多個值,例如下麵的設置等價於DirectoryIndex index.php index.html
:
DirecotryIndex index.php
DirectoryIndex index.html
如果要替換某個值,則直接修改或使用disabled關鍵字禁用其前面的Directoryindex。例如禁用index.php,只提供index.html的索引。
DirectoryIndex index.php
DirectoryIndex disabled
DirectoryIndex index.html
但註意,"disabled"關鍵字必須獨自占用一個DirectoryIndex指令,否則它將被解析成字面意思,也就是說將其當作一個index文件響應給客戶端。
DirectoryIndex指令可以設置在Server、Virtual host、Location和Directory上下文。所以,當設置在location或Directory容器中時,它將覆蓋全局設置。例如,當DocumentRoot為/usr/local/apache/htdocs時:
DirectoryIndex index.php
<directory /usr/local/apache/htdocs/newdir>
DirectoryIndex index.html
</directory>
# 或者
<location /newdir>
DirectoryIndex index.html
</location>
在輸入http://IP/newdir/
時,將提供index.html而非index.php。
當DirectoryIndex提供的索引文件都不存在時,將根據Options中的Indexes選項設置決定是否列出文件列表,除非是提供文件下載,否則出於安全考慮,這個選項是強烈建議關閉的。例如以下設置為打開,當
<directory /usr/local/apache/htdocs/newdir>
Options Indexes
DirectoryIndex index.html
</directory>
1.2.5 ServerName和ServerAlias
ServerName用於唯一標識提供web服務的主機名,只有在基於名稱的虛擬主機中該指令才是必須提供的。也就是說,如果不是在基於名稱的虛擬主機中,可以任意指定該指令的值,只要你認為它能唯一標識你的主機。但如果不設置該指令,那麼httpd在啟動時,將會反解操作系統的IP地址。
唯一標識主機的方式,也即ServerName的語法為:
ServerName {domain-name|ip-address}[:port]
例如,在主機web.longshuai.com上提供了一個httpd web服務,如果還想使用www.longshuai.com提供同樣的服務,還想效率更高點,則在設置DNS別名後再配置:
ServerName www.longshuai.com
ServerAlias用於定義ServerName的別名。如果在定義ServerName之後再定義ServerAlias,那麼ServerName和ServerName沒有任何區別。當然,為了區分基於名稱的虛擬主機,還是必須要定義ServerName。
例如,下麵幾個ServerName和ServerAlias是完全等價的。
<VirtualHost *:80>
ServerName server.example.com
ServerAlias server server2.example.com server2
ServerAlias *.example.com
# ...
</VirtualHost>
1.2.6 Include指令
在httpd啟動時,首先會解析配置文件。httpd支持include指令來包含其他文件,在解析配置文件時會進行配置合併。
支持通配符"*"、"?"和"[]",但它們不能匹配斜線"/",如有必要,它們會按照文件名的字母順序依次進行載入。如果include指令中指定包含一個目錄,則會按照字母順序載入該目錄內的所有文件,這比較容易出錯,因為有些時候會產生一些臨時文件或非配置類的文件。
例如:
Include /usr/local/apache/conf/ssl.conf
Include /usr/local/apache/conf/vhosts/*.conf
可以使用絕對路徑,也可以使用相對路徑,如果使用相對路徑,則它相對於ServerRoot。
Include conf/ssl.conf
Include conf/vhosts/*.conf
如果include包含的文件不存在時,將報錯。這時可以使用IncludeOptional指令進行載入,這表示存在則載入,不存在就算了。例如下麵的第一條指令中,如果vhosts下沒有子目錄,或者子目錄中沒有".conf"文件都將失敗,而第二條指令則不會。
Include conf/vhosts/*/*.conf
IncludeOptional conf/vhosts/*/*.conf
1.2.7 Define和UnDefine指令
該指令用於定義參數或定義向後全局生效的變數。語法格式為:
Define param [value]
當只給定一個param時,表示定義一個參數,這個參數用於< IfDefine param >容器進行判斷,只有定義了的參數param,該容器才返回真,其內封裝的指令才生效。它的等價行為是在httpd啟動時(必須是啟動時),使用"-D"選項定義參數。例如下麵兩個方法是等價的:
# startup command
shell> httpd -DMyName ......
# in config
Define MyName
當給定了兩個參數,即還指定了value時,將表示定義一個變數,該變數具有向後全局性。也就是說,定義在某個虛擬主機中的變數在後面的另一個虛擬主機中也有效。引用變數時,使用${var}
的方式。註意,變數名中不能包含冒號":"。
例如:
<IfDefine !TEST>
Define servername www.example.com
</IfDefine>
DocumentRoot "/var/www/${servername}/htdocs"
使用UnDefine指令則是取消Define定義的參數或變數。語法為UnDefine param
。
1.2.8 VirtualHost指令
無疑,這是最重要的指令之一。用於封裝一組指令只作用於指定主機名或IP地址的虛擬主機上。
語法格式為:
<VirtualHost addr[:port] [addr[:port]] ...> ... </VirtualHost>
其中addr部分可以是以下幾種情況:
- 虛擬主機的IP地址
- 虛擬主機IP地址對應的FQDN(不推薦)
- 字元"*",匹配任意IP地址
- 字元串"_default_",是"*"的別名
例如:
<VirtualHost 10.1.2.3:80>
ServerAdmin [email protected]
DocumentRoot "/www/docs/host.example.com"
ServerName host.example.com
ErrorLog "logs/host.example.com-error_log"
TransferLog "logs/host.example.com-access_log"
</VirtualHost>
需要為虛擬主機指定ServerName,否則它將會從主配置繼承。對於基於名稱的虛擬主機,ServerName更是不可缺少,否則將繼承操作系統的FQDN。
當一個請求到達時,將按照最佳匹配進行主機匹配:通配的內容越少,優先順序越高,也就越佳。例如"192.168.100.14:80"的優先順序高於"*:80"。如果基於名稱的虛擬主機無法匹配上,則採用虛擬主機列表中的第一個虛擬主機作為響應主機。如果所有虛擬主機都無法匹配上,則採用從主配置段落中的主機,如果主配置段落中註釋了DocumentRoot,則返回對應的錯誤。
具體配置方法,見配置httpd虛擬主機。
1.2.9 Options和AllowOverride指令
Options啟用或禁用指定目錄下的某些特性。有效值包括:All、None、ExecCGI、FollowSymLinks、Includes、IncludesNOEXEC、Indexes、MultiViews、SymLinksIfOwnerMatch。
不指定options時,預設為all。一般除了提供下載服務會開啟一個Indexes選項,其他選項都會關掉,即使用:
Options None
AllowOverride指令用於控制是否讀取".htaccess"配置文件。
如何設置這個指令要看具體情況,有以下幾種值,此外還可以設置為all和none,表示啟用、禁用所有特性。
- AuthConfig:基於用戶認證時設置該值,此時將可以使用AuthGroupFile, AuthName, AuthType, AuthUserFile, equire等認證相關指令。
- FileInfo: 控制文檔類型時使用該值,此時將可以使用ErrorDocument, SetHandler,以及一些URL重寫的指令。
- Indexes:控制目錄索引時使用該值,此時可以使用AddIcon, DirectoryIndex。
- Limit:是否允許使用order、allow、deny指令,這三個指令已經廢棄,目前還存在是為了相容老版本。
例如下麵的指令使得在使用非認證類和索引控制類指令時,將產生伺服器類的錯誤。
AllowOverride AuthConfig Indexes
1.2.10 Require指令
1.2.11 長連接相關指令
KeepAlive指令用於開啟和關閉長連接功能。
KeepAlive on/off
在沒有開啟長連接時,客戶端每請求一個資源都需重新建立一次TCP連接,而使用了長連接後,客戶端只需在最初請求一次TCP連接,之後就可以使用同一個TCP連接發送其他的http請求。長連接的狀態是指在服務端處理完某一個請求後,它立即進入長連接狀態以保持TCP連接不斷開,等待客戶端再次發送請求。
但長連接自身的缺陷是會一直占用著連接不釋放,所以必須得給出一個長連接的超時時間。這個超時時間由KeepAliveTimeout指令控制,進入長連接後如果在此時間間隔內客戶端還沒有發送新請求,則TCP連接自動斷開。如果在長連接狀態下,客戶端再次發送了請求,則服務端處理請求,併在處理完請求後又再次進入長連接狀態並計算KeepAliveTimeout。
此外,還可以通過指令MaxKeepAliveRequests控制每個長連接下的TCP連接的能接受的最大請求數。無疑,這個值應該設置的大一些,設置為0表示無限制。這個指令是從數量的角度控制長連接的TCP應該何時斷開。例如,在長連接超時時間內接受同一個客戶端的500個請求才斷開,然後該客戶端再有新的請求只能重新建立TCP連接。
MaxKeepAliveRequests 500
1.3 容器類指令
路徑和條件判斷容器包括:
- < Directory >、< DirectoryMatch >
- < Files >、< FilesMatch >
- < Location >、< LocationMatch >
- < IfModule >
- < IfDefine >
- < IfVersion >
- < if >
- < elseif >
- < else >
1.3.1 容器< Directory >和< Files >
還包括它們的正則匹配容器< DirectoryMatch >、< FilesMatch >。
< Directory >容器的作用是"對於匹配到的目錄,封裝一組指令,這些指令只作用於該目錄以及它的子目錄中的文件"。註意,< Directory >容器通常都是用絕對路徑,即< Directory /PATH/to/DIR >,如果使用相對路徑,則它相對於根文件系統的"/"。例如< directory newdir >等價於< directory /newdir >。
例如:
<Directory "/">
AllowOverride none
require all denied
</Directory>
<Directory "/usr/local/apache/htdocs">
require all granted
</Directory>
第一個容器表示拒絕所有對"/"下內容的訪問,包括子目錄中的文件,這個根是根文件系統的根,而不是ServerRoot。而第二個容器則表示允許/usr/local/apache/htdocs目錄下文件的訪問。
由此可以想象得出,出於安全考慮,應該總是先將父目錄進行限制,再在需要放寬許可權的子目錄中指定特定的許可權。正如上面的設置,將最頂級目錄"/"完全限制,然後在小範圍的htdocs目錄中放行。
再看< Files >容器,它針對的是某個或某些特定的能被匹配上的文件。它匹配的範圍是它所在的上下文。
例如,下麵的指令如果寫在server上下文,那麼將對任意private.html文件拒絕。
<Files private.html>
require all denied
</Files>
而如果將其寫在< directory >容器中,則只對該目錄容器中的所有private.html生效。由於< directory >會遞歸到子目錄中,所以子目錄中的private.html也會拒絕,但非private.html將被允許。
<Directory "/usr/local/apache/htdocs">
require all granted
<Files private.html>
require all denied
</Files>
</Directory>
< directory >和< files >容器可以使用通配符,"*"表示任意字元,"?"表示任意單個字元,"[]"表示範圍,如[a-z]、[0-9],但是這些通配符都不能匹配"/"。所以要跨目錄匹配時,必須顯式指定各個目錄的"/"符號。
例如,<directory /*/public.html>
無法匹配/home/user/public.html,但directory /home/*/public.html
可以匹配。
它還可以使用正則表達式匹配,只需使用一個"~"符號即可。這時和使用< DirectoryMatch >、< FilesMatch >是一樣的,只不過Match類指令不需要使用"~"符號。
例如,下麵的設置。其中後兩個Directory容器是等價的。
# 匹配不區分大小寫的gif/jpg/jpeg/png
<FilesMatch "\.(?i:gif|jpe?g|png)$">
Require all denied
</FilesMatch>
<Directory ~ "^/usr/local/apache/htdocs/[0-9]{3}">
DirectoryIndex digest.html
</Directory>
<DirectoryMatch "^/usr/local/apache/htdocs/[0-9]{3}">
DirectoryIndex digest.html
</DirectoryMatch>
需要註意的是,httpd採用的pcre庫提供的perl相容正則。以下是官方手冊提供的一個示例,使用的命名捕獲語法,它將匹配/var/www/combined/目錄下的一級子目錄,但不進行遞歸。將每個匹配到的結果保存到命名的分組sitename中,並通過環境變數"MATCH_capturename"進行引用,其中capturename必須轉為大寫字母,因為它就是這樣賦值的。
<DirectoryMatch "^/var/www/combined/(?<sitename>[^/]+)">
Require ldap-group cn=%{env:MATCH_SITENAME},ou=combined,o=Example
</DirectoryMatch>
目前已經不能使用未命名的後向引用,例如$0,$1...。在URL重寫時,正則語法至關重要,像grep/sed/awk中天然支持的基礎正則和擴展正則語法雖然能解決大部分問題,但想要實現複雜的需求,只能使用語義豐富、完整的正則,如pcre提供的正則。
1.3.2 容器< Location >
該容器和< Directory >、< Files >容器差不多,都是對滿足匹配條件的路徑封裝一組指令,這些指令只生效於這些能匹配的路徑。但是< Location >和< Directory >、< Files >最大的區別是:前者匹配的目標是WebSpace,即匹配URL中的路徑,而後兩者匹配的是本地文件系統的路徑。
例如,當設置下麵的location容器時,將匹配http://192.168.100.14/newdir/index.html
。
<Location "/newdir">
......
</Location>
location支持三種匹配模式:
- 精確匹配:location的模式和URL中的路徑部分精確對應。
- 加尾隨斜線:location的模式中加了尾隨斜線時,將匹配該目錄裡面的內容。
- 無尾隨斜線:location的模式中沒有尾隨斜線時,將匹配該目錄和目錄裡面的內容。
例如,下麵兩個容器,第一個將匹配/private1、/private1/和/private1/file.txt,但不能匹配/private1other,而第二個將匹配/private2/和/private2/file.txt,但不能匹配/private2和/private2other。
<Location "/private1">
......
</Location>
<Location "/private2/">
......
</Location>
location和sethandler指令一起使用時很方便。例如,開啟狀態信息頁面:
<Location "/server-status">
SetHandler server-status
Require all granted
</Location>
同樣,除了支持"*"、"?"、"[]"的通配符匹配,還支持"~"和LocationMatch指令的正則匹配。方法見上面的< Directory >容器。
1.3.3 < IfDefine >、< IfModule >和< IfVersion >條件判斷
這三個容器都是條件判斷容器,且都只在httpd啟動時進行判斷,判斷為真,則封裝在其內的指令生效,否則忽略。且都可以在條件前加一個"!"以實現條件的否定,而且都可以嵌套以實現更複雜的配置。
< IfModule >容器是指當啟動時載入了某模塊時,該容器內的指令生效。可以是靜態載入的模塊,或者使用LoadModule指令載入的,但如果這樣的話,載入對應模塊的LoadModule指令必須在< IfModule >指令之前。例如:
LoadModule status_module modules/mod_status.so
<IfModule "status_module">
<Location "/server-status">
SetHandler server-status
Require all granted
</Location>
</IfModule>
< IfDefine param >容器用於判斷參數param是否已經定義,如果定義了,則條件為真,封裝在其內的指令生效,否則忽略。加上感嘆號則表示取反,例如< IfDefine !param >。
那麼如何定義參數呢?有兩種方法:使用httpd命令的"-D"選項;使用Define指令。
例如,在使用httpd啟動時,加上一個"-D"選項定義MyName參數。
httpd -DMyName ......
或者在配置文件中使用Define指令進行定義,但必須在< IfDefine >容器之前定義。例如:
Define MyName
< IfDefine >可以進行嵌套。例如下麵是官方的一個示例:
httpd -DReverseProxy -DUseCache -DMemCache ...
<IfDefine ReverseProxy>
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
<IfDefine UseCache>
LoadModule cache_module modules/mod_cache.so
<IfDefine MemCache>
LoadModule mem_cache_module modules/mod_mem_cache.so
</IfDefine>
<IfDefine !MemCache>
LoadModule cache_disk_module modules/mod_cache_disk.so
</IfDefine>
</IfDefine>
</IfDefine>
< IfVersion >容器用於判斷httpd的版本。例如:
<IfVersion >= 2.4>
# this happens only in versions greater or equal 2.4.0.
</IfVersion>
1.3.4 < If >、< ElseIf >和< Else >容器
意義不言自明。< If >...< /If >判斷表達式是否為真,如果為真,則封裝在其內的指令生效;< ElseIf >...< /ElseIf >作用於< If >...< /If >之後,而< Else >...< /Else >則作用於最後。
表達式的寫法和shell腳本的表達式差不多,例如數值比較"-eq"、"-gt",字元串比較"=="、">=",以及其他一些表達式"-z"、"-n"、"-f"等,此外,它還支持正則匹配表達式"~="、"!~"。具體相關函數、變數、表達式、語法等見 http://httpd.apache.org/docs/2.4/expr.html。
例如:
# 請求首部沒有Host欄位時,該段指令將生效。
<If "-z req('Host')">
...
</If>
# 如果請求主機地址屬於0/16,則if段生效,否則如果屬於0/8,則elseif段生效,否則else生效
<If "-R '10.1.0.0/16'">
#...
</If>
<ElseIf "-R '10.0.0.0/8'">
#...
</ElseIf>
<Else>
#...
</Else>
1.4 配置文件的合併規則
配置文件的段落以一種非常特殊的順序生效。理解配置文件的合併規則非常重要,否則配置了半天可能發現根本不會生效。
以下是5個組類合併的順序:
- 1.< Directory > (正則匹配的容器除外)
- 2.< DirectoryMatch > (以及< Directory "~" >)
- 3.< Files >和< FilesMatch >同時處理
- 4.< Location >和< LocationMatch >同時處理
- 5.< If >
此外,還需要註意的一些規則是:
- 除了< Directory >容器,每個組以它們出現的順序進行合併。例如一個/foo請求可以匹配< Location "/foo/bar" >和< Location "/foo" >,它們都屬於上面列出的第4組,所以對於這兩個Location容器,誰配置在前面就匹配誰。
- < Directory >容器即上面的第一組處理的順序是先處理路徑"短"的,再處理路徑長的。這裡的短指的是離根文件系統的"/"越近就越短。由於這個組不包含正則匹配的表達式(即< Directory ~ >),所以這裡的"短"就代表它的路徑表達式短。例如< Directory "/var/web/dir" >將優先於< Directory "/var/web/dir/subdir" >被處理。
- 如果出現多個< Directory >的路徑完全一樣的極端情況,那麼將按照出現順序處理。
- 使用Include指令包含的文件將被插入到該指令的位置,然後按規則進行處理。
- < VirtualHost >段落的配置將在外部對應的段處理完畢以後再處理,這樣就允許虛擬主機覆蓋主伺服器的設置。
- 當請求是由mod_proxy處理的時候,< Proxy >容器將會在處理順序中取代< Directory >容器的位置。
需要註意的是,配置文件中的指令都是由各個模塊提供的,所以各指令是由各對於模塊來解析、處理、合併的,配置文件的作用只不過是將各個模塊的指令整合在一起方便定義。另外,上面定義的5個組別都是由httpd的核心模塊提供的,因此它們才有處理順序的要求。
當在運行時進行請求匹配,將先按照上面合併規則提供的順序進行匹配,如果某個組中出現了能成功匹配請求的模塊,將提升一次合併的層次,使得這次模塊的匹配變為第三次匹配。例如下麵的配置使用了由mod_headers提供的Header指令用於設置HTTP的首部,如果請求/example/index.html,那麼最終設置的CustomHeaderName首部的值是什麼呢?
<Directory "/">
Header set CustomHeaderName one
<FilesMatch ".*">
Header set CustomHeaderName three
</FilesMatch>
</Directory>
<Directory "/example">
Header set CustomHeaderName two
</Directory>
首先按照前面提供的合併順序匹配到"/",這會初始化設置CustomHeaderName的值為one,再匹配到/example,CustomHeaderName被設置為two。最後分組中提供的指令FilesMatch匹配成功,提升一次合併的層次,這是第三次匹配,導致CustomHeaderName最終設置為three。
下麵的例子中,如果這些指令都對請求生效,它們將按照"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>
D和E無疑是最後生效的。再看三個Directory類的容器,對於Directory和DirectoryMatch,前者先生效,所以C排在A和B後,對於A和B,虛擬主機會在外部段落處理完後再處理,所以在A和B進行合併時,B將覆蓋A,也即A先生效。所以順序為"A>B>C>D>E"。但如果將上面的A段落改為:
<Directory "/a/b">
A
<FilesMatch f.html>
D1
</Files>
</Directory>
那麼最終的順序為"A>B>C>D>D1>E"。
以下示例則更有教育意義。儘管Directory設置了更嚴格的許可權,但因為Location比Directory更後生效,它對所有訪問都不做任何限制。也就是說,Directory在這裡的許可權設置是完全多餘的。所以說,理解配置文件的合併規則對寫配置文件至關重要。
<Location "/">
Require all granted
</Location>
# Whoops! This <Directory> section will have no effect
<Directory "/">
<RequireAll>
Require all granted
Require not host badguy.example.com
</RequireAll>
</Directory>