понедельник, 2 июля 2012 г.

nginx: regex в server_name

Штатно есть весьма удобный функционал с регэкспами в server_name (далее блок), позволяющий весьма интересные трюки. (дока) Но проблема в том, что использование таких регэкспов таит много подводных камней.
Например, если мы определили .site.ru, регулярки на отдельный блок, даже если оно помещено выше главного блока, работать не будут. Вот такой прикол.
Причина в том, что у регулярок приоритет минимален и выбираются они, когда остался только блок по умолчанию (что неявно и описано в доке выше).

Для примера возьмём задачу редиректа с доменов вида www.sub.site.ru на sub.site.ru (где поддоменов sub может быть десятки и сотни). Правильнее всего вынести домены, с которых нужен редирект, в отдельный блок. Но вроде простая задача "в лоб" не решается, см. описание выше. Поэтому придуманы варианты (в порядке возрастания извращений, но не быстродействия):
0) передать заботу редиректов фронтенду. Тому же апачу.

1) в главный блок пихать
if ($host ~* ^www\.(w+\.site\.ru)$) {
set $newdomain $1;
rewrite ^(.*)$ http://$newdomain$1 permanent;
}
Но... IfIsEvil

2) улучшенный 1). Подключить встроенный перл и им обрабатывать $host

3) для каждого поддомена www.(sub).site.ru сделать отдельный блок и там return 301 sub.site.ru. Идеальный вариант, если поддоменов 1-10 и-или их количество расти не будет. Задача частично может решиться скриптами автогенерации блоков. Для нагруженных серверов пожалуй оптимальный вариант.

4) Пропихнуть в nginx патчи для поднятия приоритета регэкспам.

Но есть вариант проще, тоже вполне рабочий.
server {
    server_name ~^www\.(\w+\.site\.ru)$;
    return 301 http://$1$request_uri;
}

server {
    server_name site.ru www.site.ru ~^\w+\.site\.ru$;
    ....
}

!!! В таком описании указывать *.site.ru или .site.ru недопустимо! Отвалится первый блок.

Для облегчения работы можно в первом блоке сделать именованную переменную (иначе при первом же регэкспе во всяких там location потеряем значение), тогда будет так:
server_name ~^www\.(?<domain>.+\.site\.ru)$;
и работаем с $domain
или потом в коде установить переменную
set $newdomain $1;

Комментариев нет:

Отправить комментарий