Apache HTTP Serverの「ディレクティブ」は設定項目、「コンテキスト」はそのディレクティブを記述できる場所です。
たとえばListenは待ち受けportを決めるdirectiveで、server configにだけ書けます。DirectoryIndexはserver config、VirtualHost、directory section、.htaccessで使えます。
Listen 80
DirectoryIndex index.php index.html
Apache 2.4公式docsのContext・Overrideを読めるようになると、「このディレクティブはここでは使えません」「.htaccessに書くと500になる」といったerrorを自分で診断できます。
この記事では、4つのcontext、Directory・Files・Locationの違い、sectionのmerge順序まで順番に解説します。
Apacheのディレクティブとは
directiveはApacheの動作を設定する命令です。基本構文はdirective名の後ろへ空白区切りでargumentを渡します。
ServerName example.com
DocumentRoot "/var/www/example/public"
DirectoryIndex index.php index.html
この例ではServerName、DocumentRoot、DirectoryIndexがdirectiveです。example.comやpath、file名がargumentです。
directiveごとに次が異なります。
- 必要なargumentとsyntax
- 省略時のdefault
- 記述できるcontext
- .htaccessで必要なoverride class
- directiveを提供するmodule
- 利用できるApache version・platform
コンテキストとは
contextは、directiveが合法に使えるconfiguration上の場所です。Apache 2.4公式docsでは主に次の4種類で示されます。
| context | 意味 | 例 |
|---|---|---|
| server config | main設定fileのtop level | httpd.conf、apache2.conf、include file |
| virtual host | <VirtualHost>内 |
host・IP・portごとの設定 |
| directory | requestへmatchするsection内 | Directory、Files、Location、If、Proxyなど |
| .htaccess | directoryごとの分散設定file | /var/www/example/public/.htaccess |
「server config」は設定file内ならどこでもよいという意味ではありません。VirtualHostやDirectoryなどsection containerの外側にあるglobal contextを表します。
server configコンテキスト
server configはhttpd.confやapache2.confなどのtop levelです。Includeで読み込まれたfileも、Includeが置かれた位置のcontextとして扱われます。
# server config
Listen 80
PidFile "logs/httpd.pid"
IncludeOptional "conf.d/*.conf"
Apache 2.4のconfigurationは複数fileへ分割されることが一般的です。古い構成名を暗記するのではなく、実際のmain fileとIncludeを確認します。
httpd.confの場所、HTTPD_ROOT、Include、構文確認はApache httpd.confの設定方法で詳しく解説しています。
virtual hostコンテキスト
virtual host contextは<VirtualHost>内です。1つのApache processで、hostname・IP address・portごとに異なるsite設定を持たせます。
<VirtualHost *:80>
ServerName example.com
DocumentRoot "/var/www/example/public"
</VirtualHost>
<VirtualHost *:80>
ServerName api.example.com
DocumentRoot "/var/www/api/public"
</VirtualHost>
VirtualHostは「仮想software」ではなく、選択されたhost・address・portへ適用するconfiguration containerです。
同じdirectiveがglobalとVirtualHostの両方にある場合、VirtualHost側のsectionは対応するglobal sectionの後に適用されます。ただし最終値のmerge方法はdirectiveを提供するmoduleによって異なります。
directoryコンテキスト
Apache公式docsでcontextがdirectoryとなっているdirectiveは、一般に次のsection内で使用できます。
<Directory>/<DirectoryMatch><Files>/<FilesMatch><Location>/<LocationMatch><If><Proxy>/<ProxyMatch>
ただし、すべてのdirectory-context directiveがすべてのsectionで同じように使えるわけではありません。
代表的な例外です。
AllowOverrideはDirectory sectionだけOptions FollowSymLinks系はDirectoryまたは.htaccessOptionsはFiles / FilesMatchでは使えない
個別directiveのContextとsection固有の制約を両方確認します。
.htaccessコンテキスト
Contextに.htaccessが含まれるdirectiveは分散設定fileへ書けます。ただし、server側でAllowOverrideまたはAllowOverrideListによる許可も必要です。
# server config側
<Directory "/var/www/example/public">
AllowOverride FileInfo Indexes
Require all granted
</Directory>
# /var/www/example/public/.htaccess
DirectoryIndex index.php index.html
RewriteEngine On
Contextに.htaccessと書かれていても、Override欄のclassがserver側で許可されていなければ処理されません。
AllowOverride・AllowOverrideList・RewriteRuleの基本はApache .htaccessの書き方で実践的に解説しています。
ContextとOverrideの違い
Contextは「どの種類の場所に書けるか」、Overrideは「.htaccessで使うために必要な許可class」です。
たとえばDirectoryIndexのdocsには、次のような情報があります。
| Context | server config, virtual host, directory, .htaccess |
|---|---|
| Override | Indexes |
| Module | mod_dir |
main config、VirtualHost、Directory sectionならContextを満たせば使えます。.htaccessではさらにAllowOverride IndexesまたはAllowOverrideListによるDirectoryIndexの許可が必要です。
Apache公式ディレクティブページの読み方
directive pageの項目を上から確認します。
| 項目 | 確認内容 |
|---|---|
| Description | directiveの目的 |
| Syntax | argument、optional値、繰り返し |
| Default | directiveを省略した場合の値 |
| Context | 書けるconfiguration場所 |
| Override | .htaccessで必要なclass |
| Status / Module | core・base・extensionと提供module |
| Compatibility | 対応version・platform・追加時期 |
Default欄はApache本体がdirective省略時に使う値です。distributionが配布するdefault configに実際に書かれた値と同じとは限りません。
クイックリファレンスの記号を読む
Apache公式Directive Quick Referenceではcontextを短い記号で示します。
| 記号 | context |
|---|---|
s |
server config |
v |
virtual host |
d |
directory |
h |
.htaccess |
p |
proxy section |
たとえばsvdhなら4contextで使用可能、sだけならserver config top levelだけです。
quick referenceは一覧確認に便利ですが、細かな制約・security warning・version差は個別directive pageも開いて確認します。
Directoryはfilesystemを対象にする
Directory sectionはfilesystem pathと、そのsubdirectory・fileを対象にします。
<Directory "/var/www/example/public">
Options -Indexes
AllowOverride None
Require all granted
</Directory>
/var/www/example/publicはURLではなくserver上のabsolute file pathです。file access、Options、AllowOverrideなどfilesystemに関する設定はDirectoryを基本にします。
Directory sectionはserver configまたはVirtualHost内へ記述します。.htaccessの中へDirectory containerは書けません。
DirectoryMatchで正規表現を使う
regular expressionでfilesystem pathをmatchする場合はDirectoryMatchを使うと意図が明確です。
<DirectoryMatch "^/var/www/[^/]+/public$">
Options -Indexes
</DirectoryMatch>
regexはApacheが利用するPCRE系syntaxとして、version・escaping・path separatorを確認します。単一pathなら通常のDirectoryを優先します。
Filesはファイル名を対象にする
Files sectionはdirectoryに関係なくfile名へmatchします。
<Files "private.html">
Require all denied
</Files>
特定extensionなどregular expressionが必要ならFilesMatchを使います。
<FilesMatch "\.(bak|sql)$">
Require all denied
</FilesMatch>
特定filesystem directory内のfileだけに絞る場合は、Directoryの内側へFilesをnestedできます。
<Directory "/var/www/example/private">
<Files "report.html">
Require all denied
</Files>
</Directory>
LocationはURL webspaceを対象にする
Location sectionはclientから見えるURL pathを対象にします。filesystemへ存在しないhandler、server-status、proxy endpointなどにも適用できます。
<Location "/internal-status">
Require ip 192.0.2.0/24
</Location>
/internal-statusはURL pathです。/var/www/...のfilesystem pathではありません。
filesystem fileのaccess制御はDirectory / Filesを基本にします。Locationはmerge順でDirectory・Filesより後に評価され、重なるauthorization設定を上書きして意図せずaccessを許可する危険があります。
Proxyは転送先resourceを対象にする
Proxy / ProxyMatch sectionはreverse proxy先など、proxied resourceへ設定を適用します。
<Proxy "http://backend.example.internal/*">
Require all granted
</Proxy>
外部・内部backendへのaccess policy、authenticationなどをURL webspaceのLocationと混同せず管理します。proxy directiveが使えるcontextは個別docsで確認します。
IfとIfModuleは評価タイミングが違う
<If>はrequest時のexpressionで設定を適用します。
<If "%{REQUEST_URI} =~ m#^/admin/#">
Require ip 192.0.2.0/24
</If>
一方、IfModule・IfDefine・IfVersionはstartup・restart時にconditionを評価します。
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>
IfModuleはoptionalなmodule差を吸収するときに使います。必須directiveを囲むと、moduleがない場合に有用なstartup errorが出ず、設定が黙って無視される点に注意します。
sectionのmerge順序
1requestへ複数sectionがmatchすると、Apacheは決められた順序でmoduleごとのconfigurationをmergeします。
基本順序は次のとおりです。
- 通常のDirectoryと、許可された.htaccess
- DirectoryMatchと
<Directory "~"> - FilesとFilesMatch
- LocationとLocationMatch
- If
VirtualHost内のsectionは、対応するglobal sectionの後に適用されます。Proxy requestではProxy sectionがDirectoryの役割を置き換える場合があります。
Directoryは短いpathから長いpathへ
通常のDirectory sectionはconfiguration fileの記述順だけではなく、短いdirectory componentから長いものへ処理されます。
<Directory "/var/www">
Require all denied
</Directory>
<Directory "/var/www/example/public">
Require all granted
</Directory>
同じpathへ複数Directoryがmatchする場合や、別種類のsectionが重なる場合はさらにmerge ruleが関係します。
「後勝ち」と決めつけない
mergeは単純なtext上書きではありません。各moduleがdirectiveの値をどうcombineするか決めます。
たとえばOptionsの相対指定、authorizationのAuthMerging、rewrite rule inheritanceなどは固有のruleがあります。最終結果が不明な場合は、個別module docsと実際のrequest・logで確認します。
sectionのnested可否
一部sectionはnestedできます。
- FilesはDirectory内へ置ける
- IfはDirectory・Location・Files内へ置ける
- Ifを別のIfへ直接nestedする構成には制約がある
- VirtualHostはserver config top levelで定義する
「HTML tagのように見えるから自由に入れ子にできる」と考えず、section directive自身のContextとApache Configuration Sections docsを確認します。
同じdirectiveを別contextへ書いた例
DirectoryIndexは複数contextで使えます。
# server全体のdefault
DirectoryIndex index.html
# 特定VirtualHost
<VirtualHost *:80>
ServerName app.example.com
DirectoryIndex index.php index.html
</VirtualHost>
# 特定filesystem directory
<Directory "/var/www/docs">
DirectoryIndex home.html
</Directory>
# .htaccess。AllowOverride Indexesが必要
DirectoryIndex landing.html
同じdirectiveでも、どのrequestへ適用したいかでcontextを選びます。
RewriteRuleはcontextで動作が変わる
RewriteRuleはserver / VirtualHost contextと、Directory / .htaccessのper-directory contextでpatternの対象が異なります。
.htaccessでは配置directoryのprefixと先頭slashが除かれます。
# /shop/.htaccess で /shop/products/42 にmatch
RewriteRule "^products/([0-9]+)$" "product.php?id=$1" [END,QSA]
同じ文字列を別contextへ移動するだけでは同じ動作にならない代表例です。pattern・substitution・flagの詳細はApache RewriteRuleの基本を参照してください。
context errorの診断
Directive is not allowed here
directiveを許可されないcontextへ書いています。公式directive pageのContextを確認し、server top level、VirtualHost、Directory、.htaccessの正しい場所へ移します。
Invalid command
directive名のtypo、提供moduleがloadされていない、Apache versionに存在しないdirectiveの可能性があります。
apachectl -M
apachectl -V
loaded moduleとversionを確認します。
.htaccessで500になる
Contextに.htaccessが含まれるか、Override classがAllowOverrideで許可されているか、AllowOverrideListにdirective名があるかを確認します。
.htaccessはrequest時にparseされるため、main configのsyntax checkが成功しても、実際に対象URLへrequestして初めてerrorが分かる場合があります。
設定が反映されない
別VirtualHostが選ばれている、filesystem pathとURL pathを混同している、より後のLocation / Ifで値がmergeされた、別.htaccessやinclude fileがある可能性を確認します。
設定を検証するcommand
main server configはreload前にsyntaxを検証します。
apachectl -t
apachectl -S
apachectl -M
-t |
設定fileのsyntax |
|---|---|
-S |
VirtualHostのparse結果 |
-M |
loaded module |
.htaccessは対象URLへrequestし、HTTP statusとerror logを確認します。startup error・request error・module別logの調べ方はApacheログの場所と確認方法で解説しています。
Context確認チェックリスト
- directiveの個別docsでSyntax・Context・Overrideを確認した
- quick referenceのs / v / d / h / pを読める
- server configはsection外のglobal contextだと理解した
- VirtualHostはhost・address・port単位のconfiguration containerとして使った
- filesystemはDirectory / Files、URLはLocationで分けた
- .htaccessではAllowOverride・AllowOverrideListも確認した
- IfとIfModuleの評価timingを分けた
- Directory→DirectoryMatch→Files→Location→Ifのmerge順を確認した
- 単純な後勝ちと決めつけず、module固有のmerge ruleを確認した
- main configはsyntax・vhost・module、.htaccessは実requestとlogで検証した
まとめ
Apacheのdirectiveは設定項目、contextはそのdirectiveを合法に記述できる場所です。公式docsではserver config、virtual host、directory、.htaccessを確認し、.htaccessの場合はOverrideとAllowOverrideも照合します。
Directoryはfilesystem、Filesはfile名、LocationはURL webspace、Proxyはproxied resourceを対象にします。複数sectionがmatchすると、Directory・DirectoryMatch・Files・Location・Ifの順でmoduleごとにmergeされます。
設定例をコピーする前にContext・Module・Compatibilityを確認し、main configはapachectl、.htaccessは実際のHTTP requestとerror logで検証してください。