Nginx: ネストしたlocationのconfigの継承動作
以前、ネストしたlocation設定があった場合、どのようにlocationが選択されるのかを解説しました(こちら)。 今回は、あるlocationが選択された場合、その親のlocationのconfigが子のlocationにどのように継承されていくのかを解説します。
今回見ていく設定は以下のような構造になります。
今回見ていく設定の例
location / { XXXX(何らかの設定) location /l1/ { } location /l2/ { XXXX'(何らかの設定) } }
location /の配下に/l1/と/l2/のlocationがネストしています。
/l1/にアクセスした場合はlocation /l1/にマッチしますが、どのような設定になるのでしょうか?
XXXXが反映される?されない?
一方、/l2/へアクセスした場合はどのような設定になるのでしょうか?
XXXX'とXXXXがマージされる?上書きされる?
これは、"configの種類による。そしてどのconfigがどのような動作になるかはドキュメントには書かれていない。"が答えになります。
参考になるページ
"ドキュメントにはない"と言いましたが、いくつか参考になるページはあるので、あげておきます。
(1) Directive Inheritance in Nested Location Blocks
ネストしたlocationでディレクティブがどのように継承されるかを説明したstack overflowのページ。ディレクティブを以下の3種に分類してそれぞれの動作を説明しています。
- Standard Type
- Array Type
- Command Type
(2) Understanding the Nginx Configuration Inheritance Model
(1)と同じようにディレクティブの分類ごとに動作を説明しています。こちらはserverディレクティブからの継承も説明していたり(1)より少し詳しい情報が載っています。
(3) Document behaviour for all config statements in nested location blocks
ネストされたlocationに継承されるディレクティブとそうでないディレクティブがあるので、それらをドキュメント化してほしいという提案とそれに対する回答。
回答を要約すると以下のようになります。
全てのディレクティブはカレントレベルに定義がなければ、前(上位)のレベルから継承される。ただし以下のようないくつかの例外がある。
rewriteモジュールのディレクティブ: break, if, return, rewrite, set
これらのディレクティブについてはこちらに文書化されている。
最終的なlocation処理であることを指定するディレクティブ: proxy_pass, fastcgi_pass, grpc_pass, scgi_pass, uwsgi_pass, memcached_pass, perl, empty_gif, flv, mp4, stub_status
これらのディレクティブはlocationレベルでのみ使用可能であり、locationのネストがサポートされるまで、継承の対象外だった。この点は十分に文書化されていないかもしれないが、概念自体は明白だ(*1)。
try_filesディレクティブ
本来どのように動作すべきなのかわからない(*2)。この動作はバグとみなされるかもしれない(*3)。
ブロックレベルのディレクティブで、追加の構成コンテキストを導入するもの:特にlocation自体、limit_except、location内でのif。
Nginxのバグトラッカーの情報なので、これが一応公式ドキュメントといえばそうかもしれません。
最後に、F5のブログ記事から。
「間違い4: ディレクティブ継承の仕組みを忘れる」に配列型ディレクティブの継承動作に関する勘違いしやすい点について注意事項があります。
子のlocationで同種の配列ディレクティブ(add_headerとか)を指定した場合、親コンテキストの値に追加されると勘違いしがちな点を指摘しています。正しくは、親コンテキストの値に追加するのではなく、子コンテキストの値が親の値を上書きします。これは後で例を示します。
これらを要約すると以下のようになります。
- ディレクティブは基本的にカレントレベルに定義がなければ、前(上位)のレベルから継承される。ただしいくつかの例外となるディレクティブがある((3)参照)
- 配列型ディレクティブを子コンテキストで指定した場合は、配列への追加ではなく上書き動作になる
実際の動作
実際によく使う設定を例にいくつか動作を見ていきましょう。
✓ add_header ディレクティブ
add_headerの継承設定
location / { add_header X-foo foo; location /l1/ { } location /l2/ { add_header X-bar bar; } }
add_headerは上で説明したいわゆる配列型ディレクティブになり、以下のような動作になります。
- カレントのlocationに設定がなければ親の設定が引き継がれる。
- カレントのlocationに設定があればその設定が採用される。この場合、親の設定は無視される(追加ではなく上書き)。
表1 add_headerの継承動作
アクセスURL | 返されるヘッダー |
---|---|
/ | X-foo: foo |
/l1/ | X-foo: foo |
/l2/ | X-bar: bar |
/l1/アクセス時は親のlocationの設定が継承され、X-foo: fooが返されます。
一方、/l2/アクセス時は子の設定で上書きされ、X-bar: barが返されます。 ディレクティブ名がadd_headerとなっていて勘違いしやすいですが、追加ではなく設定が上書きされてX-bar: bar ヘッダのみ返されます。
✓ allow,deny ディレクティブ
これもadd_headerと同じ配列型ディレクティブで、add_headerと同じような動作をします。
allow, denyの継承設定
location / { allow 127.0.0.1; deny all; location /l1/ { } location /l2/ { allow 192.168.167.1; deny all; } }
表2 allow, deny の継承動作
アクセスURL | 有効な設定内容 |
---|---|
/ |
allow 127.0.0.1; deny all; |
/l1/ |
allow 127.0.0.1; deny all; |
/l2/ |
allow 192.168.167.1; deny all; |
✓ try_files ディレクティブ
try_filesは子のlocationには引き継がれません。
try_filesの継承設定(継承はされない)
location / {
try_files $uri $uri/ /_index.php?$query_string;
location /l1/ {
}
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
この設定では、/fooなどにアクセス時は_index.phpに内部リダイレクトされますが、"location /l1/" 内ではtry_filesは継承されず無効なので、/l1/fooアクセス時は_index.phpは呼び出されません。
✓ auth_basicモジュールのディレクティブ
Basic認証の設定を行うauth_basicモジュールにはauth_basic, auth_basic_user_file の二つのディレクティブがあります。これらは通常のディレクティブなので、親のlocationの設定が引き継がれます。カレントのlocationに設定があれば上書きされます。
auth_basicの継承設定
location / { auth_basic "private"; auth_basic_user_file parts/htpasswd.txt; location /l1/ { } location /l2/ { auth_basic "private l2"; } }
表3 Basic認証設定の継承動作
アクセスURL | 有効な設定内容 |
---|---|
/ |
auth_basic "private"; auth_basic_user_file parts/htpasswd.txt; |
/l1/ |
auth_basic "private"; auth_basic_user_file parts/htpasswd.txt; |
/l2/ |
auth_basic "private l2"; auth_basic_user_file parts/htpasswd.txt; |
/l1/においてもauth_basic, auth_basic_user_file ディレクティブが継承されるので、Basic認証は有効となります。
/l2/においては、指定されているauth_basicの設定だけ上書きされます。auth_basic_user_fileの設定はlocation /のものが継承されます。
まとめ
ネストされたlocation設定がある状況で、ディレクティブがどのように子locationに継承されるのか公式なドキュメントがない中で参考になるページを例示しました。
また、通常のディレクティブ、配列型ディレクティブ、例外的に継承されないディレクティブ(try_files)の実際の継承動作例を示しました。これで、ネストしたlocation設定での継承動作のイメージがつかめたでしょうか。
Nginxの設定はシンプルなようでいて、locationの選択処理が複雑だったり、今回のようなディレクティブの継承動作がUndocumentedだったりして、誤った設定をしがちです。設定後はしっかりテストをするようにしましょう。
私はconfigを修正する度に毎回手動でテストするのも面倒なので、簡単なスクリプトを作成して、指定URLにリクエストを送信し、期待したレスポンスが返ってくるかチェックできるようにし、これをユニットテストの要領で自動で繰り返しテストできるようにしています。
参考: ソースコードの追い方
各ディレクティブの継承動作を確認するにはソースコードを確認する手もあります。参考までに取っ掛かりになる情報だけ記載しておきます。
Nginxの各モジュールはngx_http_module_tという構造体にConfigを処理する関数群を登録しています。この構造体のmerge_loc_confフィールドにlocationのconfigマージ用の関数が登録されています。
各モジュールのソースはsrc/http/modules/にあるので、各.cのmerge_loc_confに登録されている関数を除いてみると、だいたい何をやっているかわかると思います。 accessモジュールのallow, denyディレクティブなら ngx_http_access_module.c の ngx_http_access_merge_loc_conf() 関数がそれです。小さい関数なので、カレントに何らかの設定があればそれを採用し、なければ親の設定を参照しているのがわかると思います。
[関連記事]
Nginxパズル: ネストされたlocationのマッチング動作
(*1) これらのディレクティブは元々、処理の終端となるlocationに設定されるものであるので、子のlocationを持つ必要はないはず/子のlocationへの継承は不要という意味と考えられる。
(*2) 下位のlocationに設定が継承されない動作のことを言っていると思われる。
(*3) リンク先には関連チケットへのリンクもあります。
投稿日:2025/08/05 10:16