要件1
- HTTP リクエストヘッダー User-Agent: により、スマートフォンか PC かを判別し、ドキュメントルートを切り替える。
- PC 用ドキュメントルート = /var/www/public_html
- スマートフォン用ドキュメントルート = /var/www/public_html/sp
よく見かける例
http {
...
server {
...
set $is_sp 0;
if ($http_user_agent ~* "(iphone|ipod|android.*mobile)") {
set $is_sp 1;
}
root /var/www/public_html;
location / {
if ($is_sp) {
root /var/www/public_html/sp;
}
}
}
}
間違いではない。でも...
同じことを、もう少しかっこよく
http {
...
server {
...
if ($http_user_agent ~* "(iphone|ipod|android.*mobile)") {
set $path_prefix "/sp";
}
location / {
root /var/www/public_html$path_prefix;
}
}
}
解説
root ディレクティブには変数が使える。ただそれだけのこと。
要件2
- HTTP リクエストヘッダー User-Agent: により、スマートフォンか PC かを判別し、ドキュメントルートを切り替える。
- PC 用ドキュメントルート = /pub/www/public_html
- スマートフォン用ドキュメントルート = /pub/www/public_html/sp
ここまでは要件1と同じ。今回はそれに加えて、
-
スマートフォン用ドキュメントルートを優先してコンテンツを探す
- スマートフォンで見たときに、スマートフォン用ドキュメントルートにファイルが存在すればそれを応答し、存在しなければ PC 用ドキュメントルートから探して見つかったらそれを応答する。
このように、スマートフォン用に最適化されたドキュメントが作られていたらそれを表示し、なければ PC 用コンテンツをそのまま表示したいというのは、コンテンツ制作現場では実践的でありがちな要件だと思う。
http {
...
server {
...
if ($http_user_agent ~* "(iphone|ipod|android.*mobile)") {
set $path_prefix "/sp";
}
location / {
root /var/www/public_html$path_prefix;
try_files $uri $uri/ @pc;
}
location @pc {
root /var/www/public_html;
}
}
}
解説
try_files
ディレクティブで、コンテンツの探索順序を設定している。try_files $uri $uri/
までは普通で、その後ろに @pc
という名前付きロケーションを設定している。
@pc
ではドキュメントルートを PC 向けに設定しているので、「最初にスマートフォンのドキュメントルートを探し、なければ PC のドキュメントルートを探す」という、今回の要件を満たす動きになる。
ここで、こんな疑問が湧いてくるのではないだろうか。
Q1: PC で見たときは $path_prefix
が未設定なので、PC 向けドキュメントルート /var/www/public_html を2回探すことになるんじゃないの?
そのとおりである。
たしかに無駄な処理かもしれない。しかし実際にはその無駄な処理にかかるオーバーヘッドは、たかがしれている。もしかすると Nginx 内部では動作が最適化されていて、無駄な処理自体行われていないかもしれない。(ソースを読んでいないのでわからないけど)
Q2: スマートフォンで見たときだけ /var/www/public_html/sp を探しに行けば良いのだから、if ブロックに try_files
を書けば良いのでは?
try_files
は if ブロックには書けない。http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files によると、try_files
は server または location ブロックにしか書けないのである。