Nginx の location ブロックのパターン化

簡単そうに見えたのだが思い通りにいかない.

優先順位というか, 処理順序というか. ここらの話.

location  = / {
  # matches the query / only.
  [ configuration A ] 
}
location  / {
  # matches any query, since all queries begin with /, but regular
  # expressions and any longer conventional blocks will be
  # matched first.
  [ configuration B ] 
}
location /documents/ {
  # matches any query beginning with /documents/ and continues searching,
  # so regular expressions will be checked. This will be matched only if
  # regular expressions don't find a match.
  [ configuration C ] 
}
location ^~ /images/ {
  # matches any query beginning with /images/ and halts searching,
  # so regular expressions will not be checked.
  [ configuration D ] 
}
location ~* \.(gif|jpg|jpeg)$ {
  # matches any request ending in gif, jpg, or jpeg. However, all
  # requests to the /images/ directory will be handled by
  # Configuration D.   
  [ configuration E ] 
}

Nginx location priority - Stack Overflow

なので公式 wiki/recipe を見ながら少し整理.

PHP FastCGI Example | NGINX

Codeigniter | NGINX

Drupal | NGINX

osCommerce | NGINX

WordPress | NGINX

公式にて書式はこう.

Syntax: location [ = | ~ | ~* | ^~ ] uri { ... }

Module ngx_http_core_module

複数のlocationブロックがある場合, リクエストのURL内該当文字列に対して優先順位の順番にマッチを確認していき, 最初にマッチしたロケーション設定(処理)ひとつが反映される.

1.「=」完全一致
2.「^~」前方検索 (マッチ文字列の長い順)
3.「~」正規表現 (記述順)
4.「~*」正規表現 (記述順・大小文字を考慮しない)
5.「(なし)」前方検索 (マッチ文字列の長い順)

これに加えて, それぞれの設定(処理)自体の優先順位を考慮しておかないと, 「アクセス拒否したつもりが丸出し」や「PHPスクリプトのダウンロード公開」などとなってしまう.

1. 特殊ファイルのログ制御
2. ディレクトリ単位のアクセス拒否
3. ファイル単位のアクセス拒否
4. PHPファイルの実行 (PHP-FPM)
5. 静的ファイルのキャッシュ期限の設定
6. フロントコントローラへのリクエスト付け替え

これら2つの優先順位を考慮にいれながら書かれている公式wiki/recipeの設定は, ある程度おおまかにパターン化できる.

1.「=」特殊ファイルのログ制御
2. 「^~」ディレクトリ単位のアクセス拒否
3.「~」ファイル単位のアクセス拒否
4.「~」PHPファイルの設定・実行 (PHP-FPM)
5.「~*」静的ファイルのキャッシュ期限の設定
6.「なし」フロントコントローラへのリクエスト付け替え

頻出しそうなのを順序を守って箇条書きに.

# 1. specific files 
location = /favicon.ico {
  log_not_found off;
  access_log off;
}

location = /robots.txt {
  allow all;
  log_not_found off;
  access_log off;
}

# 2. deny directories
location ^~ /conf/ {
  return 403;
}

# 3. deny files
location ~ (^|/)\. {
  return 403;
}

location ~ \.(txt|log)$ {
  allow 192.168.0.0/16;
  deny all;
}

# 4. php scripts
location ~ [^/]\.php(/|$) {
  fastcgi_split_path_info ^(.+?\.php)(/.*)$;
  if (!-f $document_root$fastcgi_script_name) {
    return 404;
  }
  fastcgi_pass unix:/var/run/php5-fpm.sock;
  fastcgi_index index.php;
  include fastcgi_params;
}

# 5. static content files
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
  expires max;
}

# 6. others(internal rewrite)
location / {
   try_files $uri $uri/ index.php;
}

実際はこれらに加えてさらに, 内部/外部での リライト/リダイレクトなどを記述して location 設定を複数またいでいくことになると, 大量に記述された apache mod_rewrite 設定のように直感的に設定できなくなっていくので locationブロックに関してはテンプレ化しておくのがいいように思う.

そもそもは「if」や「locationを使った入れ子」で混乱したので.

nginxのlocation設定について、優先順位の基本と意外な罠

If Is Evil | NGINX

if ($request_uri = /) { 
  set $test  A; 
} 
  
if ($host ~* teambox.com) { 
  set $test  "${test}B"; 
} 
  
if ($http_cookie !~* "auth_token") { 
  set $test  "${test}C"; 
} 
    
if ($test = ABC) { 
  proxy_pass http://teambox-cms.heroku.com; 
  break; 
} 

nginx hack for multiple conditions

手をつける前に, ある程度の歴史や経緯を分かっておく必要があったかな.

Nginx Configuration Primer

まあいいか.

みんなも reload したらチェックしてみよう.

$ curl -L http://yourhost.com/ -o /dev/null \
     -w '%{http_code} %{content_type} %{size_download} %{url_effective}\n' \
     -s

200 text/html; charset=UTF-8 1334 http://yourhost.com/