nginx連載5回目: nginxの設定、その3 - locationディレクティブ

前回と同様に、バーチャルサーバの設定について説明を行います。特に今回はURIのパス名毎の設定を行うlocationディレクティブと関連する設定について説明します。

特に次の設定について説明します。

  • location
  • alias
  • index
  • try_files
  • error_page
  • internal

URIのパス毎の設定 - location

location / {
    [locationコンテキスト]
}

locationディレクティブではURIのパス毎の設定を記述できます。括弧{ }で囲まれた部分がlocationコンテキストになります。リクエストURIのパスがこのlocationディレクティブのパスの条件に一致した場合にこのlocationコンテキストに記述した設定が適応されます。パスの条件の評価方法は前方一致と正規表現の2つです。locationコンテキストにlocationディレクティブを記述してネストすることもできます。

上記の例ではリクエストURIのパスの先頭が"/"に一致した場合に適応されます。すべてのパスは"/path/to/page.html"のように"/"で始まるため、他のlocationディレクティブのパスの条件に一致したもの以外はこのlocationディレクティブが適応されます。

locationディレクティブではURIのパスの前に"=", "~", "~*", "^~"の4種類のプレフィックスを付けることができます。

location プレフィックス URIのパス {
    [locationコンテキスト]
}

それぞれの意味は次のようになります。

プレフィックス説明
なし前方一致
^~前方一致。一致したら、正規表現の条件を評価しない。
=完全一致。パスが等しい場合。
~正規表現(大文字・小文字を区別する)
~*正規表現(大文字・小文字を区別しない)

locationディレクティブはパスの条件が評価されて選ばれたものが適応されます。この条件はパスの文字列の前方一致あるいは正規表現による評価です。この評価の順番は以下のようになります。

  1. 前方一致("=", "^~", プレフィックスなし)の条件の評価を実施
    1. 最も一致する条件を選ぶ。
    2. 選ばれた条件が、完全一致で、プレフィックスが"="であれば、そこで評価を終了し、そのlocationディレクティブを適応する。
    3. 選ばれた条件のプレフィックスが"^~"であれば、そこで評価を終了して、そのlocationディレクティブを適応する。
  2. 正規表現("~", "~*")の条件の評価を実施
    1. 正規表現の条件を設定ファイルに定義した順番に評価する。一致したら、そこで評価を終了して、そのlocationディレクティブを適応する。
  3. 前方一致の評価で選ばれた条件のlocationディレクティブを適応する。

ここで注意して欲しいことがあります。前方一致の条件が先に評価されますが、プレフィックス無しの条件が選ばれたときには、後で評価される正規表現の条件に一致したものが無かった場合に、選ばれたlocationディレクティブが適応されるということです。この点を理解していないと、パスが一致したのに何で適応されないんだろうと悩むことになります。

いくつかの例で確認してみましょう。

まず、プレフィックスがない場合です。次の例では、リクエストURIのパスが"/example/page.html"であるときには設定Bが適応されます。それ以外の場合は設定Aが適応されます。単純に最長一致したものが適応されることがわかります。

location / {
    [設定A]
}

location /example/ {
    [設定B]
}

次の例は、"="プレフィックスがある場合です。リクエストURIのパスが"/"であるときには、設定Bが、"/page.html"であるときには設定Cが適応されます。それ以外の場合は設定Aが適応されます。例外的なページを指定する場合やlocationのパスの評価を早く確定したい場合に役に立つでしょう。なお、設定Bでは、indexディレクティブが有効であれば"/index.html"などに内部リダイレクトするため、最終的には設定Aが適応されます。

location / {
    [設定A]
}

location = / {
    [設定B]
}

location = /page.html {
    [設定C]
}

次の例は、プレフィックスがない場合と"~"プレフィックスがある場合です。リクエストURIのパスが"/example/page.html"であるときには、プレフィックスなしのパスが完全に一致していますが、正規表現の条件が優先されるため、設定Bが適応されます。

location /example/page.html {
    [設定A]
}

location ~ ^/example/ {
    [設定B]
}

今度は"^~"プレフィックスを付けてみます。リクエストURIのパスが"/example/page.html"であるときには、設定Aのパスに一致し、評価が確定するため、設定Aが適応されます。

location ^~ /example/page.html {
    [設定A]
}

location ~ ^/example/ {
    [設定B]
}

なお、正規表現の条件は次の例のように拡張子に一致させたいときに使うことが多いです。

location ~ \.php$ {
    [設定A]
}

正規表現を使うときには、括弧( )で囲うことにより後方参照することができます。次の例ではphpのファイル名を設定Aの中で"$1"として参照することができます。

location ~ ^/example/(.*\.php)$ {
    [設定A]
}

なお、リクエストURIのパスは様々な機能によ正規化され書き替えられた後のものがlocationディレクティブのパスの条件の対象となります。例えば、URIエンコードされていれば、デコードした後のパスが対象になります。

名前付きロケーション

locationディレクティブではURIのパスの代わりに次のように@の後に名前を付けることにより、名前付きロケーションを作ることができます。この名前付きロケーションは後述する内部リダイレクトで使用します。内部的なURIのパスの書き換えを行わずに内部リダイレクトを行う際に利用されます。

location @名前 {
    [locationコンテキスト]
}

なお、この名前付きロケーションはserverコンテキストにしか記述できません。

ロケーションのパスの割り当て - alias

aliasディレクティブではlocationディレクティブで指定したURIのパスをファイルシステム上のパスに対応させます。

次の例では、URIのパス"/s/"を"/var/www/s/images/"に対応させます。このとき、リクエストのパス"/s/page.html"と"/s/file.jpg"は、それぞれファイルシステム上のパス"/var/www/s/html/page.html"と"/var/www/s/images/file.jpg"に対応することになります。

location /s/ {
    alias /var/www/s/html/;
}
location ~ ^/s/(.+\.(?:gif|jpg|png)$) {
    alias /var/www/s/images/$1;
}

このaliasディレクティブは、先の例のようにURIのパスがrootディレクティブで指定したディレクトリからのパスと異なるときに利用します。

次の例では、aliasディレクティブで記述していますが、これはパス"/s/"がaliasディレクティブで指定した"/var/www/html/s/"の最後の"/s/"の部分と一致しています。"/var/www/html"をrootとしたときのパスと考えてもよいでしょう。

location /s/ {
    alias /var/www/html/s/;
}

このときは、次のようにrootディレクティブで書き直した方がわかりやすいでしょう。

location /s/ {
    root /var/www/html;
}

内部リダイレクト

普通のリダイレクトはレスポンスコードに301や302を、Locationヘッダフィールドにリダイレクト先のURIを指定して返し、クライアントはそのURIに対して再びリクエストを送ります。これとは別に内部リダイレクトというものがあります。これは、レスポンスコードに301や302を指定せずに、内部的にURIのパスの書き換えを行い、その結果のページの内容を返します。クライアントから見るとリダイレクトしているようには見えません。nginxではこのような内部リダイレクトがよく使われます。

次のセクション以降で説明するindexディレクティブ、error_pageディレクティブ、tri_filesディレクティブではこの内部リダイレクトが使われます。内部リダイレクトではリダイレクト先のパスに対して毎回locationディレクティブの評価が行われます。

インデックス - index

indexディレクティブにはリクエストのURIが"/"で終わっている(つまりディレクトリになっている)ときにインデックスとして使われるファイル名を設定します。記述方法は次の通りです。

index ファイル名 [ファイル名... [フォールバック]];

httpコンテキスト、serverコンテキスト、locationコンテキストに記述できます。なお、パスへのファイル名の追加は内部リダイレクトとして行われます。デフォルトの設定値は"index index.html;"であり、"index.html"がパスに追加され、そのパスへ内部リダイレクトします。

次のような設定の場合には、リクエストURIのパスが"/example/"のときに、index.htmlというファイルが存在すれば、"/example/index.html"に内部リダイレクトします。index.htmlが存在せず、index.phpが存在すれば、/example/index.phpに内部リダイレクトします。

location /example/ {
    index index.html index.php;
}

設定の一番右に、"/"で始まるファイル名のパスを書くと、ファイルが存在しなかったときにフォールバックするURIのパスになります。次のように記述すると、index.htmlやindex.phpが存在しなかったら、/index.phpにリダイレクトします。

location /example/ {
    index index.html index.php /index.php;
}

ファイルの存在チェック - try_files

try_filesディレクティブには存在をチェックするファイルやディレクトリと存在しなかったときにリダイレクトするURIのパスを指定します。

記述方法は次の通りです。

try_files ファイル ... パス;
try_files ファイル ... =コード;

try_filesという名前の通り、指定したファイルやディレクトリの存在を順番に調べ、存在すれば、そのファイルやディレクトリに対応したファイルを返します。一つも存在しなかったら、最後に記述したパスに内部リダイレクトします。パスの代わりに名前付きロケーションを指定することもできます。"=コード"を指定したときには指定した応答コードが返されます。

このtry_filesディレクティブはバックエンドのウェブアプリケーションと連携するためによく使われます。次の例はPHPのウェブアプリケーションを処理する例です。

## URIのパスに対するファイル(静的コンテンツ)が存在すれば、そのファイル返す。
## 存在しなければ、動的コンテンツとして@webappに内部リダイレクトする。
location / {
    try_files $uri $uri/ @webapp;
}

## 拡張子がphpであるファイルに対して処理する。
location ~ \.php$ {
    fastcgi_pass    unix:/var/run/php-fpm.sock;
    fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include /etc/nginx/fastcgi_params;
}

## 動的コンテンツとして処理する。
location @webapp {
    fastcgi_pass    unix:/var/run/php-fpm.sock;
    fastcgi_param   SCRIPT_FILENAME /var/www/html/index.php;
    include /etc/nginx/fastcgi_params;
}

エラーページ - error_page

error_pageディレクティブには指定したエラーコードが発生したときに表示するページのURIを指定します。

記述方法は次の通りです。

error_page コード ... [=[レスポンスコード]] uri;

コードには300〜599までの数値を記述できます。"=レスポンスコード"を記述すると、指定したレスポンスコードを返します。"="だけを記述すると内部リダイレクト先のシステムから受け取ったレスポンスコードを返します。URIにパスだけ指定すると内部リダイレクトし、フルで指定するとLocationヘッダ フィールドによりリダイレクトします。名前付きロケーションも使えます。

例えば、次のように記述すると、サーバのエラーが発生したときには"/50x.html"ページへ内部リダイレクトします。

error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root   /usr/share/nginx/html;
}

リダイレクト先には外部のURIを指定することもできます。

error_page 500 502 503 504 http://example.jp/sorry.html;

nginxがバックエンドサーバにつながらないときなどにこの機能は便利でしょう。

また、アクセス制御により、アクセスが拒否された場合に表示するページを用意する場合にも使えます。

error_page 403 /forbidden.html
location = /forbidden.html {
    internal;
    allow all;
}

このerror_pageディレクティブはバックエンドのウェブアプリケーションと連携するときにも使われます。次の例はPHPのウェブアプリケーションを処理する例です。

## URIのパスに対するファイル(静的コンテンツ)が存在すれば、そのファイル返す。
## 存在しなければ、動的コンテンツとして@webappに内部リダイレクトする。
location / {
    error_page 404 = @webapp;
}

## 拡張子がphpであるファイルに対して処理する。
location ~ \.php$ {
    fastcgi_pass    unix:/var/run/php-fpm.sock;
    fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include /etc/nginx/fastcgi_params;
}

## 動的コンテンツとして処理する。
location @webapp {
    fastcgi_pass    unix:/var/run/php-fpm.sock;
    fastcgi_param   SCRIPT_FILENAME /var/www/html/index.php;
    include /etc/nginx/fastcgi_params;
}

内部リクエストの指定 - internal

internalディレクティブは、それを記述したロケーションが内部リダイレクトのように内部からのリクエストのためのみに使われることを宣言します。外部から直接アクセスすることはできません。

次の例では、404エラーが発生したときに/404.htmlに内部リダイレクトします。リダイレクト先のロケーションでは外部から直接このページのURIを指定してアクセスさせたくはないので、internalを記述します。

error_page 404 /404.html;
location = /404.html {
    internal;
}

バーチャルサーバの設定のまとめ

前回と今回はバーチャルサーバの設定について説明してきました。各設定が適応される順番についてまとめてみましょう。

  1. serverコンテキストの選定
    • listenディレクティブとserver_nameディレクティブにより適応するserverコンテキストが選ばれる。
  2. locationコンテキストの選定
    • locationディレクティブの条件により適応するlocationコンテキストが選ばれる。
    • ただし、indexディレクティブ、tri_filesディレクティブ、error_pageディレクティブなどにより、内部リダイレクトが行われると、再びlocationディレクティブが評価される。

今回はここまでです。設定編の残りとしては次のものを予定しています。

  • SSL/TLSの設定
  • よく使われる設定(認証、アクセス制御、rewriteモジュール等)

これが終わったら、ウェブアプリケーションとの連携編に入ります。