Posted at

備忘録: nginx でアクセス元 IP レンジに応じて Web アプリケーションを切り替える

More than 1 year has passed since last update.

あるイントラネット用の Web アプリケーションのアップグレードを試していて、次のようなことをしたい場合があった。


  1. 特定の IP からのアクセスでは次バージョンのサイトに接続する (試験ユーザ)

  2. それ以外の IP からのアクセスは現バージョンのサイトに接続する (通常ユーザ)

ローカルホストで試験するという手段もあるが、

A. ホスト名に依存する処理がある (外部の認証機構からリダイレクトする、など)

B. HTTPS 限定のサイトである (このために証明書等をローカルホストに移すのは手間かつセキュアでない)

C. 複数人のアクセスを想定している (ので人数分の /etc/hosts を書き換えるのも容易ではない)

といった条件もあり「リバースプロキシ側 (nginx) でアクセス元の IP レンジに応じて Web アプリケーションを切り替える」という方針で進めることにした。なおかつ、なるべく手軽にやりたかった。

内容はとてもシンプルだけど、すぐ忘れそうなので、備忘録として残しておく。


こう書いた

今回の要旨と関係のないディレクティブは全て省略した。

登場人物は 3 つ。

普通のユーザに見せたい方のサイトを usual_server, 試験者のサイトを testing_server と定義した。


/etc/ngnix/sites-available/site.example.com.conf

upstream usual_server {

server 127.0.0.1:8080;
}

upstream testing_server {
server 127.0.0.1:8081;
}

geo $maintenance {
default 0;
# 許可したい IP レンジ
192.168.100.105/32 1;
}

server {
if ($maintenance = 0) {
set $target_proxy_server "usual_server";
}

if ($maintenance = 1) {
set $target_proxy_server "testing_server";
}

location / {
proxy_pass http://$target_proxy_server;
}
}


あとは

$ sudo service nginx configtest && sudo service nginx reload


なぜこのような書き方にするか

If Is Evil にも書かれているように、 nginx では location context 内での if はうまく処理できない問題がある。

下記は動作しない。


だめな例

if ($maintenance = 1) {

location / {
proxy_pass http://$testing_server;
}
}
if ($maintenance = 0) {
location / {
proxy_pass http://$usual_server;
}
}

また、 nginx の if には else に対応するものがない。

「複雑なことは if で制御しようとせず、なるべくなら使わない、使っても set くらいにする」と捉えておけば問題ないはず。