Pull to refresh

nginx, пользовательские поддомены и rewrite

Reading time 3 min
Views 47K
В самых разнообразных веб-проектах возникает задача организации пользовательских поддоменов «на лету». При использовании nginx приходит на помощь следующая конструкция:
server {
    listen 80;
    server_name example.com *.example.com;
    location / {
        root   /var/www/example.com/$subdomain;
        index  index.html index.php;
    }
    set $subdomain "";
    if ($host ~* ^([a-z0-9-\.]+)\.example.com$) {
        set $subdomain $1;
    }
    if ($host ~* ^www.example.com$) {
        set $subdomain "";
    }
}
Сама по себе она не нова и упоминалась уже на хабре. Тем не менее остался не рассмотренным вопрос организации rewrite для пользовательских поддоменов.Сразу обращаю ваше внимание, что решаемая задача — организация различных rewrite для небольшого количества пользовательских поддоменов.Основная проблема применения правил rewrite для конкретного поддомена заключается в том что часто мы вынуждены использовать два if. Первый — для явного задания поддомена, второй — является частью самого правила. Например:
if (!-e $request_filename) {
  rewrite ^(.+)$ /index.php?q=$1 last;
}
К сожалению, на даннный момент nginx не позволяет использовать ни вложенные конструкции if ни оператор «И» в них. Таким образом прийдется пойти на небольшую хитрость.Создадим в папке с конфигами nginx (у меня во FreeBSD это /usr/local/etc/nginx, в Linux, как я подозреваю, /etc/nginx) файлик checks.conf следующего содержания:
set $f "|f";
set $e "|e";
set $d "|d";

if (!-f $request_filename) {
    set $check $check$f;
}
if (!-e $request_filename) {
    set $check $check$e;
}
if (!-d $request_filename) {
    set $check $check$d;
}
set $check $subdomain$check;
Что он делает? фактически — помещает в переменную $check имя текущего поддомена, дописав в конец “флаги” относительно нашего запроса (т.е. если -f истинно — допишется “|f”, если -e истинно то “|e” и т.д.)Теперь организуем использование этого файла для поддоменовДобавим в наш конфигурационный файл следующую строку:
location / {
   root   /var/www/example.com/$subdomain;
   index  index.html index.php;
   include example.com/*.conf;
}Создадим в папке с конфигами nginx каталог example.com. В нем будут хранится правила rewrite для каждого поддомена (по одному на файл, отсутствие файла означает что rewrite для данного поддомена не нуженВ качестве примера приведу содержимое подобного файла для поддомена docs на котором запущена DokuWiki:
if ($subdomain = "docs") {
    rewrite ^(/)_media/(.*) $1lib/exe/fetch.php?media=$2 last;
    rewrite ^(/)_detail/(.*) $1lib/exe/detail.php?media=$2 last;
    rewrite ^(/)_export/([^/]+)/(.*) $1doku.php?do=export_$2&id=$3 last;
}

include service/checks.conf;

if ($check ~* ^docs.*[f].*$) {
    rewrite ^(/)(.*)?(.*)  $1doku.php?id=$2&$3 last;
    rewrite ^(/)$ $1doku.php last;
}
Сам файл состоит из двух частей. Первую часть (до include) составляют безусловные rewrite. Мы могли бы их вынести в сам конфиг пользовательских доменов, но хранить все яйца в одной корзине в нашем случе удобнееВторая часть (после include) в работе идентична тому, что в нормалных условиях было бы записано в виде if (!-f ....). Очевидно что если бы нам необходима была проверка вида -e достаточно заменить один символ в нашем regexp.К сожалению, данный метод не позволяет решить еще одну проблему при использовании автоматических пользовательских поддоменов — а именно ограничения доступа к тем или иным папкам сайта путем базовой аутентификации (аналог .htaccess). Это связано с тем, что директива location в которой осуществляется задание подобных правил недопустима внутри оператора if.P.S. Это моя первая статья на Хабре, если есть замечания — я с радостью постараюсь их учесть
Tags:
Hubs:
+1
Comments 27
Comments Comments 27

Articles