Nginx の location の書き方で注意すること

石川英典

プライム・ストラテジー「KUSANAGI」開発チームの石川です。

KUSANAGIでも採用している nginx は非常に高性能なWebサーバです。
しかし、Webサーバとして長い間使われてきた Apache HTTP Server と比較すると設定ファイルの記述が難しい部分があります。

Apache HTTP Serverでは .htaccess ファイルなどを利用して、リダイレクトやディレクトリ・ファイル毎の個別の処理を行えます。
この際には DirectoryFiles ディレクティブを用いて対象となるディレクトリやファイルを指定します。

これに相当する nginx の設定が location ディレクティブです。
今回は location ディレクティブの適用順番を例を含めて説明します。


. htaccess を使いたいケースがある場合は、Nginx 単体で寄せるだけでなく「Nginx をフロントにして Apache httpd を裏で動かす」構成も選択肢になります。KUSANAGI では、Nginx をリバースプロキシとして動かしつつ、Apache httpd 側の .htaccess などの機能と併用できる構成が用意されています。

KUSANAGIのWebサーバ構成(nginxリバースプロキシ + httpd など)
https://kusanagi.tokyo/document/kusanagi-web-servers/


location の書き方の種類

location ディレクティブと対象のURI (および正規表現) の間に指定する modifier によって、5種類 の書き方があります。

  1. 完全一致 =
  2. 正規表現 ~
  3. 大文字小文字を区別しない正規表現 ~*
  4. 正規表現を適用させない一致 ^~
  5. modiferなしの一致

完全一致 =

文字通り URI が完全一致した際に適用される location です。

特定の処理を行う決まったファイルや決まったディレクトリに使われます。

正規表現 ~ と 大文字小文字を区別しない正規表現 ~*

URI のディレクトリ名やファイル名の条件として正規表現を用いて適用させる location です。

大文字小文字を区別しない (例えば .jpg と .JPG など) 場合は ~* を用います。

正規表現を適用させない一致 ^~

ちょっと特殊な location です。後で説明します。

modifierなしの一致

location と URI の間にmodiferを書かない場合です。一般的な location の書き方になります。
しかし、上記の4つの書き方によって影響を受けます。

location の適用の優先順位

location は以下の優先順位で評価されます。

  1. ネストされない限り、locationは最終的に一箇所のみが適用される
  2. 完全一致 = と正規表現 ~ ~*最初にマッチしたもの が優先される
  3. modifierなしの一致と正規表現を適用させない一致 ^~最長マッチしたもの が優先される
  4. modifierなし、よりも、正規表現 ~ ~* が優先される
  5. 正規表現を適用させない一致 ^~ より下のURIには正規表現が適用されない
  6. いずれの location にも一致しない場合、URIがディレクトリの場合は index に従って処理される
  7. いずれの location にも一致しない場合、URIはファイルとして処理される

ここでのポイントは 最初にマッチしたもの が優先される location と 最長マッチしたもの が優先される location があることです。


優先順位の挙動は、実際の設定ファイルで確認するのが確実です。検証環境を用意する場合は、まず素の構成(WordPressの基本プロファイル)を作成し、location を1つずつ追加して「どの location が最終的に適用されたか」を確認すると整理できます。

KUSANAGI のクイックスタートページでは、Webサーバ(nginx / httpd)とPHPバージョンを選んで初期設定し、そのまま WordPress をプロビジョニングするところまでの手順がまとまっています。

▼ KUSANAGI クイックスタート
https://kusanagi.tokyo/document/kusanagi-quickstart/


location の適用例

nginx の 公式ドキュメント の例をもとに説明します。

location = / {  
    [ configuration A ] 
}  
location / {  
    [ configuration B ] 
}  
location /documents/ {  
    [ configuration C ] 
}  
location ^~ /images/ {  
    [ configuration D ] 
}  
location ~* \.(gif|jpg|jpeg)$ {  
    [ configuration E ] 
}
  • / :
    • 完全一致 = / により configuration A
      • ここでマッチが終了するので modifier なし / には進まない
  • /index.html :
    • modifierなし / により configuration B
  • /documents/document.html :
    • modifierなし / にマッチ
    • modifierなし /documents/ にマッチ
      • modifierなしは 最長マッチ が優先されるため、modifierなし /documents/ により configuration C
  • /images/1.gif :
    • modifierなし / にマッチ
    • 正規表現を適用させない一致 ^~ /images/ にマッチ
    • 大文字小文字を区別しない正規表現 ~*\.(gif|jpg|jpeg)$ にマッチするが、正規表現を適用させない一致 ^~ /images/ の配下のURIのため、除外される
      • modifierなしと正規表現を適用させない一致 ^~最長マッチ が優先されるため、 正規表現を適用させない一致 ^~ /images/ により configuration D
  • /documents/1.gif :
    • modifierなし / にマッチ
    • modifierなし /documents/ にマッチ
    • 大文字小文字を区別しない正規表現 ~*\.(gif|jpg|jpeg)$ にマッチ
      • modifierなし、よりも、正規表現 ~* が優先されるため、 大文字小文字を区別しない正規表現 ~* \.(gif|jpg|jpeg)$ により configuration E

詳細はnginx の 公式ドキュメント を参照してください。

location の適用のポイント

これを整理すると以下のようになります。

  • 最初のマッチが優先されるlocation
    • 完全一致 =
      • 特定のファイルのURIに対しての処理で使うこと
      • 特定のディレクトリには使わない (indexディレクティブから /index.php を呼ぶことを期待する処理ができない)
      • マッチさせたい優先順に前に書く
    • 正規表現 ~ ~*
      • 特定のファイルのURIに対しての処理で使うこと
      • 特定のディレクトリには使わない (最初のマッチで適用されるため、modifierなしをオーバーライドしてしまう)
      • マッチさせたい優先順に前に書く
  • 最長マッチが優先されるlocation
    • 正規表現を適用させない一致 ^~
      • 特定のディレクトリのURIに対しての処理で使うとよい
      • 特定のファイルには使わない (indexディレクティブから /index.php を呼ぶことを期待する処理ができない)
      • マッチしたパス以下には正規表現 ~ ~* location が適用されない
      • 正規表現 ~ ~* location から除外したいディレクトリのみを書くこと
      • できるだけ厳密なマッチを書く
    • modifierなし
      • 特定のディレクトリのURIに対しての処理で使うとよい
      • 特定のファイルには使わない (正規表現が優先されてしまう)
      • 適用後に正規表現 ~ ~* が適用されてしまう場合があることを忘れないこと
      • できるだけ厳密なマッチを書く
  • どこにも一致しなかった場合
    • ディレクトリの場合は index ディレクティブに従う
      • その結果 index.php で処理されることになると、index.phpをURIとしてマッチを再度やり直す
    • そうでなければ、ファイルとして扱う

また location 内に try_files ディレクティブが存在すると、 /index.php のように再度URIのマッチが実行される場合があります。

KUSANAGI の nginx 設定ファイル

以下は KUSANAGI でプロビジョンした WordPress 用プロファイルの設定ファイルの抜粋です。
また、注釈としてコメントを入れています。

server {
    # HTTP用の設定

    listen 80;
    listen [::]:80;

    # 途中省略

    include conf.d/505.inc;
    include conf.d/favicon.inc;
    include conf.d/acme.inc;

    include conf.d/profile.wp.inc;
    include conf.d/static.inc;
}

server {
    # HTTPS用の設定

    include conf.d/ssl_listen.inc;

    # 途中省略

    include conf.d/505.inc;
    include conf.d/favicon.inc;
    include conf.d/acme.inc;

    include conf.d/profile.wp.inc;
    include conf.d/static.inc;
    include conf.d/fcache_purge.inc;
}

KUSANAGI の nginx の設定ファイルでは上記のように include ディレクティブを用いて、共通となる設定ファイルを外出しにして読み込む仕組みになっています。

location については、HTTPとHTTPSで処理が同じため、ここではHTTPを例に説明します。

まず、上記で include している設定ファイルの中で location に関する部分を抜粋しました。
文中にコメントで解説を入れています。

server {
    # HTTP用の設定

    listen 80;
    listen [::]:80;

    # 途中省略

    # include conf.d/505.inc;
    location = /50x.html {
        # A
        # /50x.html は Nginxのエラーページのテンプレートです。
        # 省略
    }

    # include conf.d/favicon.inc;
    location = /favicon.ico {
        # B
        # /favicon.ico はブラウザのブックマークに表示されるアイコンです。
        # 省略
    }

    # include conf.d/acme.inc;
    location ~* /\.well-known {
        # C
        # Let's Encrypt のSSL証明書を設定する際に認証情報 (challenge file) が格納される場所です。
        # 省略
    }

    # include conf.d/profile.wp.inc;
    index index.php index.html index.html;
    # URLがディレクトリの場合には、ディレクトリ内で上記の順にファイルを検索して表示します。
    location / {
        # D
        # アクセスされた際のデフォルトの設定です。
        # まず URL をファイルとして解釈して存在すればそれを表示します。
        # ファイルが存在しなければ、ディレクトリとして解釈し、上記 index に従ってファイルを探します。
        # それもなかった場合には /index.php (WordPressのエントリーポイント) を実行します。
        try_files $uri $uri/ /index.php?$args;
    }
    location ~* /\. {
        # E
        # .htaccess や .htpasswd など . から始まるファイルです。
        # セキュリティ上の理由からアクセスできないようにします。
        # 省略
    }
    location ~* /(?:uploads|files)/.*\.php$ {
        # F
        # WordPressの uploads ディレクトリなど画像などが配置されるディレクトリに
        # PHPファイルが置かれても実行できないようにします。
        # 省略
    }
    location ~* /wp-login\.php|/wp-admin/((?!(admin-ajax\.php|images/)).)*$ {
        # G
        # WordPressの管理画面のURLです。
        # 途中省略
        location ~ [^/]\.php(/|$) {
            # H
            # 拡張子が .php のものは fastcgi (php-fpm) で処理します。
            # 省略
            fastcgi_pass 127.0.0.1:9000;
            # 省略
        }
    }
    location ~ [^/]\.php(/|$) {
        # I
        # 拡張子が .php のものは fastcgi (php-fpm) で処理します。
        # 省略
        fastcgi_pass 127.0.0.1:9000;
        # 省略
    }

    # include conf.d/static.inc;
    location ~* \.(jpg|jpeg|gif|png|webp|css|js|swf|ico|pdf|svg|eot|ttf|woff|woff2)$ {
        # J
        # 画像やPDF、Webフォントといった静的ファイルです。
        # 省略
    }
}
  • 最初のマッチが優先されるlocation
    • 完全一致 =
      • A
      • B
    • 正規表現 ~ ~*
      • C
      • E
      • F
      • G
        • G の配下 *.php では H が適用され I が適用されることがない
      • H
      • I
      • J
  • 最長マッチが優先されるlocation
    • 正規表現を適用させない一致 ^~
      • なし
    • modifierなし
      • D : try_files によって以下のようになる
        • ディレクトリの場合は index を参照し index.php ならそのURIで再マッチ
          • I になる場合がほとんどと考えられる
        • ファイルの場合はそのまま表示
        • ディレクトリもファイルのない場合は /index.php で処理されるため、そのURIで再マッチ
          • I になる場合がほとんどと考えられる

例えば /.well-known/img.jpg というURIは、一見 J に該当しそうですが、正規表現は最初のマッチが優先されるため C で処理されます。
同様に、 /.well-known/test.php というURIは、一見 I に該当しそうですが、正規表現は最初のマッチが優先されるため C で処理されます。
また /.hidden.jpg というURIは、 J ではなくて先にマッチする E で処理されます。
このように、ディレクトリを正規表現でマッチさせると、その下のファイルを拡張子で判定できない場合があります。


設定を追加・変更したあとは、文法チェックを通してから反映すると切り分けがしやすくなります。KUSANAGI 環境であれば、nginx の設定チェックと反映をコマンドで実行できます。

・設定ファイルのチェック(テストのみ)
# kusanagi nginx --test

・設定の再読み込み(既存セッションを切断せずに反映)
# kusanagi nginx --reload

▼ kusanagi nginx コマンド(オプション一覧)
https://kusanagi.tokyo/document/commands/nginx/


まとめ

KUSANAGI の nginx の設定ファイルに location を追加した際に思ったように動作しない場合は、既存の location の記述が優先されているためです。

ポイントに記載したルールを参考に見直してみてください。

  • 完全一致、正規表現は最初のマッチが優先されるので、前に記述する
  • modifierなし は最長マッチが優先されるので、他にマッチしている長い location がないかチェックする
  • location の入れ子では、入れ子の中の location よりも外の location にマッチすることがある

また、Nginx の location でアクセス制限をかける際には間違いやすい落とし穴があります。こちらに関しては 別に記事 を書いていますので合わせて読んで理解を深めてください。

参考
Module nginx_http_core_module, location directive

▼WordPressに関連する脆弱性情報を毎週日本語で公開する「Security Advisory for WordPress」配信中!
https://kusanagi.biz/wordpress-security-advisory/

関連記事

Webサイト運用の課題解決事例100選 プレゼント

Webサイト運用の課題を弊社プロダクトで解決したお客様にインタビュー取材を行い、100の事例を108ページに及ぶ事例集としてまとめました。

・100事例のWebサイト運用の課題と解決手法、解決後の直接、間接的効果がわかる

・情報通信、 IT、金融、メディア、官公庁、学校などの業種ごとに事例を確認できる

・特集では1社の事例を3ページに渡り背景からシステム構成まで詳解