Динамические поддомены с использованием nginx+apache

  • Tutorial
Этот топик — очередной топик про реализацию динамических поддоменов на сайте, коих много в интернете и даже есть пара топиков на хабре.

Проблема в том, что этот вопрос везде освещается только с точки зрения перенаправления с поддомена в папку и вся динамичность поддомена заключается в том, что ты создал папку — поддомен у тебя заработал.

Иногда же требуется решение другой проблемы — например вынос на поддомен профиля пользователя и всего функционала, который с ним связан.

Например, у нас есть готовый сайт, на котором работают профили по такому url: www.example.com/users/username, и есть всякие дополнительные возможности (например www.example.com/users/username/contact и другие страницы, связанные с этим юзером).

И мы теперь хотим вынести все, что связано с юзером, на поддомен, например username.example.com, username.example.com/contact и т.д.)

Решения, которые были найдены в интернете, меня не удовлетворили по 2 причинам:
  • Не нашел решения как заставить ее работать, сохранив работоспособность домена www.example.com
  • Все найденные решения подходят только для перенаправления в папку и не работают если дальше должны работать какие то правила


На нашем сайте стоит nginx над апачем (как и на многих других), поэтому пришлось изобретать велосипед самому, используя эту связку (nginx+ apache, благо сейчас почти на всех крупных сайтах стоит проксирующий nginx над апачем)



В общем то решение простое — т.к. на сайте уже налажена через mod_rewrite работоспособность ссылок вида www.example.com/users([a-zA-Z_]+) то было принято решение делать рерайт поддоменов через nginx.

Дополнительное условие — наш сайт работает только как ww.example.com, а example.com редиректит на www.

Соответственно осталось просто написать правило в конфиге nginx для рерайта поддоменов. Правило получилось такое это решение — не верное, использовать его не рекомендуется:
                location / {
                        proxy_pass http://11.22.33.44:8080;
                        proxy_redirect http://www.example.com:8080/ /;
                        proxy_set_header Host $host;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Real-IP $remote_addr;

                        # учитываем что обязательно нужен www
                        set $uid "www";
                        # берем uid из поддомена
                        if ($host ~* "^(([a-z0-9\-]+)\.example\.com)$") {
                                set $uid $2;
                        }
                        # рерайтить нужно только если uid не www, на www работает весь сайт
                        if ($uid !~ "^(www)$") {
                                rewrite ^(.*)$ /users/$uid$1 break;
                        }
                }

upd После публикации топика BlackWizard подсказал лучшее решение, которое отвечает всем изначальным условиям:
server {
    server_name   www.example.com;

    location / {
         proxy_pass 11.22.33.44:8080;
    }
}

server {
    server_name   ~^(?<user>[a-z0-9\-]+)\.example.com$;

    location / {
        proxy_pass 11.22.33.44:8080/users/$user$uri$is_args$args;
    }
}


Таким образом, если посетитель зашел на поддомен то nginx это определяет и запрашивает из апача уже адрес вида www.example.com/users/username, а апач уже дальше разбирает все в соотвествии со своими правилами mod_rewrite.

Полученное в итоге решение обладает следующими плюсами:
  • Нет проблемы с www
  • Легко внедряется на любом сайте с уже готовой системой ссылок (не будем рассматривать процесс измененения ссылок на самом сайте)
  • Работает как для папок, так и для url, которые используют mod_rewrite


Минусы:
  • Требуется проксирующий nginx


В целом решение мне показалось довольно неплохим, готов выслушать рекомендации и критику от гуру, потому что сам я в деле настройки веб-серверов новичек, и возможно решение пригодится новичкам вроде меня.

UPDкак сделать чтобы username.example.com работал без указания всех возможных доменов в конфиге веб-сервера
Чтобы сервер корректно обрабатывал динамические поддомены, необходимо добавить одну маленькую запись в настройки DNS. Это можно сделать, используя панель управления сервером.
Просто добавьте следующую запись формата A («A record» в англоязычной версии):
*.example.com

Запись нужно добавлять после всех поддоменов (mail, smtp и т.д.)
Support the author
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 50

    0
    есть мысль обойтись одной регуляркой и ифом. Как бы задать такое условие?
    if ($host ~* "^(не www)\.example\.com$") {
    rewrite…
    }
      0
      тоже о таком думал, но не смог составить простую регулярку, которая бы это делала.

      Поэтому остановился на таком костыле, который позволяет обойти ограничение, что нельзя использовать логический оператор «и» и вложенные if в nginx
        0
        Логику вложенных if можно сделать установкой переменных.

        if условие {
        set a «a»;
        }

        if условие2 {
        set $aa «a»;
        }

        if $a==«aa» {
        и т. д.
        }
          0
          я так и сделал вообще-то

          set $uid «www»;

          if ($host ~* "^(([a-z0-9_\-]+)\.example\.com)$") {
          set $uid $2;
          }

          if ($uid !~ "^(www)$") {
          rewrite ^(.*)$ /users/$uid$1 break;
          }
        +1
        if ($host ~* "([a-z0-9\-]+)(?<!^www)\.example\.com$") {
            rewrite
        }
          0
          птичку забыл вначале ^([a-z0-9\-]+)(?<!^www)\.example\.com$
        0
        Спасибо, добавил в израбнное на будущее.
          0
          Спасибо, сейчас стоит такая задача — подыскиваю наиболее подходящий вариант решения.
            0
            Это же достаточно элементарно. А вот про минусы не понял :/, вы раньше апачем напрямую всё отдавали?
              0
              Я нет, но есть проекты, где только апач без проксирующего nginx.

              Я не спорю что это элементарно, но решения в таком виде нагуглить не удалось, поэтому решил оставить на будущее — вдруг кому пригодится.
                0
                Т.е для вас это плюс, а вы пишите минус? :)
                  0
                  Для меня это не минус и не плюс, а просто использование возможностей того ПО, которое у меня есть.

                  Для кого-то это может оказаться минусом, потому что придется устанавливать и конфигурировать доп. ПО
              +3
              Еще надо учесть, что нижнее подчеркивание нельзя использовать в поддоменах.
              Еще одна статья в тему
              Пользовательские поддомены
                0
                собственно, мое решение основано на этой статье, только там не решена проблема с www — там делается редирект, а я предложил способ, как сохранить работоспособность www

                Про нижнее подчеркивание — исправил регулярку в статье
                  0
                  можете аргументированно пояснить, почему нельзя?
                    0
                    Ответ из старого топика из обсуждений

                    «Интересные имена со знаком „_“ :) Особенно в поддоменах. IE не берет с такого домена куки, а опера ругается, что введенный урл не соответствет стандартам (потому что не соответствует) и не грузит страницу совсем. „

                    Сейчас попробовал набрать
                    www_123.ya.ru/
                    www-123.ya.ru/

                    По первому адресу 400 Bad Request
                    По второму — 404
                      0
                      С яндексом пример некорректен. Что касается утверждения выше, то видно, что вы самостоятельно его не проверяли.

                      Есть много работающих ресуров, где с указанными браузерами описанных проблем не наблюдается. Например, модуль субдоменов для движка Livestreet установлен на множестве сайтов, ни от кого не поступало таких жалоб да и тестирование также не выявило никаких проблем кроме эпизода с написанием доменов в разных регистрах (сафари).

                      Я не знаю, о каких «стандартах» говорил тот хабраюзер, но если открыть RFC1738 (Uniform Resource Locators), то можно прочесть следующее:
                      Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
                      reserved characters used for their reserved purposes may be used
                      unencoded within a URL.

                      Вы можете указать, какой конкретно стандарт запрещает использовать символ "_" для субдоменов?
                        0
                        Не путайте URL и Domain Names. В URL "_" допустим, в доменном имени — нет. Конкретно стандарт RFC1035, цитирую:
                        2.3.1. Preferred name syntax
                        <domain> ::= <subdomain> | " "
                        <subdomain> ::= <label> | <subdomain> "." <label>
                        <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
                        <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
                        <let-dig-hyp> ::= <let-dig> | "-"
                        <let-dig> ::= <letter> | <digit>
                        <letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
                        <digit> ::= any one of the ten digits 0 through 9


                        The labels must follow the rules for ARPANET host names. They must
                        start with a letter, end with a letter or digit, and have as interior
                        characters only letters, digits, and hyphen
                        . There are also some
                        restrictions on the length. Labels must be 63 characters or less.


                          –1
                          2.3.1. Preferred name syntax


                          Я не увидел здесь прямого запрета, а вы? Предпочтительный синтаксис не означает строгого следования этим правилам. С документом ошибся, вы правы.
                            0
                            Ок, есть еще RFC1123 «Requirements for Internet Hosts». Читаем:
                            2.1 Host Names and Numbers
                            The syntax of a legal Internet host name was specified in RFC-952 [DNS:4].
                            Открываем RFC-952:
                            ASSUMPTIONS
                            1. A «name» (Net, Host, Gateway, or Domain name) is a text string up
                            to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus
                            sign (-), and period (.)
                            . Note that periods are only allowed when
                            they serve to delimit components of «domain style names». (See
                            RFC-921, «Domain Name System Implementation Schedule», for
                            background). No blank or space characters are permitted as part of a
                            name. No distinction is made between upper and lower case. The first
                            character must be an alpha character. The last character must not be
                            a minus sign or period.

                            Достаточно или еще гуглом для вас поработать? ;)

                            Все эти стандарты в некотором смысле рекомендательные, т.е. нет службы полицаев, которые посадят или оштрафуют за то, что вы им не следуете. Так что всегда находятся люди, которые вертели на стержне все эти RFC и рекомендации как делать предпочтительно, им пофиг, у них своё видение как всё должно работать. Слава богу их меньшинство, иначе интернета могло бы и вовсе не быть в том виде, в котором мы его знаем, а были бы кучки разрозненных анклавов.
                              0
                              Спасибо за потраченное время, но документы 1985 года в наши дни выглядят несколько архаично. Не исключено даже, что есть какие-то более новые документы, регулириующие эти вопросы. Я попробую вечером их найти.

                              Исходя из ваших слов, их на стержне вертит даже ICANN, что доказывает вот этот домен, который серьёзно превышает лимит в 24 символа: thelongestlistofthelongeststuffatthelongestdomainnameatlonglast.com
                                0
                                В том же RFC1035 указано не 24 символа для имени а 63 для сегмента (label), т.е. этот лимит расширили. Это прям в моем предыдущем комментарии написано. Так что ICANN никого не вертит.
                                  0
                                  Сегменты с вхождением "_" обнаружить нетрудно:

                                  $ dig beta._domainkey.google.com TXT
                                  $ dig beta._domainkey.google.com TXT
                                  $ dig beta._domainkey.google.com TXT
                                  $ dig _spf-a.microsoft.com TXT
                                  $ dig _domainkey.cern.ch TXT


                                  Получается, вертят крупнейшие игроки IT? Да, это не записи типа A, но всё же полноправные labels.

                                    0
                                    $ dig _domainkey.ebay.com TXT
                                    $ dig _domainkey.yahoo.com TXT
                                    $ dig _domainkey.yandex.ru TXT
                                      0
                                      Вот и стандарт от 2000 года нашёлся, который это дело разрешает: www.ietf.org/rfc/rfc2782.txt
                                        0
                                        Я не конца понял о чем этот стандарт, но судя по смыслу в нем предлагается использовать _ для специальных целей, что скорее даже подчеркивает тот факт, что _ нельзя использовать просто так от балды. Тут как бы сразу два нарушения — сам факт использования подчерка и расположение его на первом месте, где может быть только буква или цифра. Т.е. с таким же успехом они могли взять $, который тоже не разрешен в именах доменов или % и это бы не означало, что его теперь можно использовать где угодно.
                                        Мутная тема ;)
                                          0
                                          Я привёл это в качестве развенчания мифа, что "_" нельзя использовать в сегментах (узлах) DNS. Как видно, он широко используется в Sender Policy Framework.
                                            +1
                                            В общем мне очень понравилась наша с вами дискуссия, весьма конструктивная я считаю. Можно немного подытожить:

                                            1. Нифига не понятно :)
                                            2. Есть рекомендации по синтаксису доменных имен, в них "_" не рекомендован, во избежание проблем с клиентским софтом (так сказано в RFC1035)
                                            3. Есть старые документы, прямо запрещающие использование "_" в именах хостов, доментов и т.п.
                                            4. Есть более новые документы, разрешающие "_" в строго определенной позиции(первым символом) и в специальных DNS-записях, т.е. это алиасы на сервисы.
                                            5. Есть чисто прикладные проблемы связанные в "_". Насколько я помню, squid, например, возможно не во всех версиях, попросту не пропускает через себя клиентов с запросами на такие домены. Админы на работе с этим сталкивались, это реальный случай.

                                            Итого, не знаю как вы, но я сделал вывод, что все таки "_" лучше не использовать, и кстати я не вижу никаких проблем, чем дефис хуже подчерка, непонятно.

                                            Спасибо вам за интересную беседу ;)

                                              +1
                                              Можно использовать для сайтов NSFW. За пример со сквидом спасибо, изучу этот вопрос
                                                0
                                                Если узнаете что-нибудь новое — дайте знать
                                  0
                                  К тому же есть практическая сторона. Rucenter не позволит вам зарегистриовать доменное имя с "_" и если попробуете проверить такое доменное имя на свободность, выдаст ошибку:
                                  Название домена должно состоять более чем из одного символа, начинаться и заканчиваться буквой латинского алфавита или цифрой. Промежуточными символами могут быть буквы латинского алфавита, цифры или дефис.
                                  Т.е. для регистратора это правило очевидно (если знаете какого нибудь регистратора, который позволяет — напишите). Понятное дело, что домен второго уровня ничем принципиально от третьего (четвертого, пятого...) не отличается и правила должны быть едиными.
                                    0
                                    Мы сейчас говорим о субдоменах, то есть о сегментах четвёртого уровня в иерархии DNS. Контролируя домен второго уровня, вы можете создать произвольный субдомен без участия регистратора.
                                      0
                                      Регистрацию доменов в зоне .ru регулирует регламент, который запрещает всё, кроме a-z,0-9,-
                                      Регалмент может быть основан на стандартах, но он никоим образом не может диктовать владельцу домена, какие ему создавать и поддерживать субдомены.
                                        0
                                        Вы хотите сказать что по чистой случайности этот регламент совпадает с «предпочтениями» в RFC1035? ;)
                                          0
                                          Ни в коем случае ;)

                                          Неясно лишь, почему нельзя использовать то, что на самом деле успешно работает.
                      –1
                      ISPmanager нормально работает с nginx и без там это делается в 2 клика:

                      Выбираем нужный домен — добавить запись-

                      Имя: *
                      Тип: A
                      Адрес: Ip адрес сайта

                      На мастерхосте в админке помнится тоже делалось аналогично, создавался поддомен *.example.com, указывающий на основную папку домена.

                      Остальное — работа самой CMS, правила в .htaccess.

                      И будет хоть username.example.com, хоть username.users.example.com, хоть super-article.news.example.com
                        0
                        как раз правила в .htaccess это самое интересное, dns прописать дело 2 минут
                          0
                          Смотря как CMS сделана, и для чего требуются динамические поддомены. .htaccess и тот не всегда будет нужен — можно обойтись и вообще без его изменения. Только с CMS немного повозиться, что бы ссылки автоматом заменяла и соображала что показывать на главной странице поддомена, когда больше никаких параметров в url не задано.
                            0
                            А смысл?.. Я вот тоже хотел предложить использовать А или CNAME.
                              0
                              Ну я имею в виду что добавление A записи *.example.com — дело 2 минут, а вот наладить сервер чтобы работали все рерайты как надо для поддоменов — уже сложнее (особенно если надо не просто рерайтить поддомен в папку а для поддомена сделать чтобы были работоспособны все человеко-понятные адреса страниц, которые уже есть на сайте, т.е. сделать рерайт вида username.example.com => example.com/users/username => example.com/users.php?page=about&name=username)

                              Мое решение позволило мне вынести на поддомен довольно большое количетсво страниц (страница с контактами, анкета, еще несколько), приэтом не переписывая заново все рерайты в .htaccess а просто добавив несколько строк в конфиг nginx
                          0
                          Дописали, бы в статье своей что нужно еще сделать для того чтоб username.example.com работал без указания всех возможных доменов в конфиге веб-сервера.
                            +11
                            Статья как делать НЕ НАДО — if,rewrite это зло.

                            Правильное решение:

                            server {
                                server_name   www.example.com;
                            
                                location / {
                                     proxy_pass 11.22.33.44:8080;
                                }
                            }
                            
                            server {
                                server_name   ~^(?<user>[a-z0-9\-]+)\.example.com$;
                            
                                location / {
                                    proxy_pass 11.22.33.44:8080/users/$user$uri$is_args$args;
                                }
                            }
                            
                              0
                              Спасибо, за совет, завтра с утра проверю ваш вариант.
                                0
                                Этот вариант работает и он правильный. А за использование вашего надо бить по рукам ;)
                                  0
                                  ну я первый раз в жизни конфигурировал nginx :)

                                  Настроил так, как смог, поэтому и опубликовал топик, расчитывая на то, что подскажут как сделать то же самое, но лучше.

                                  Топик обновил.
                                0
                                А просто example.com как обработать?
                                  +1
                                  думаю как-то так
                                  server {
                                  server_name www.example.com, example.com;
                                  либо отдельно
                                    0
                                    Теперь в избранное.
                                    Спасибо.
                                +1
                                А вас не беспокоит, что, к примеру, у вас остаются рабочими обе ссылки:
                                example.com/user
                                и
                                user.example.com/

                                И для них URL для ссылок, стилей и скриптов могут указывать на разные файлы.
                                <a href="/contacts.html">контакты</a> — /var/www/contacts.html | /var/www/example.com/user/contacts.html

                                  0
                                  Не беспокоит, у меня настроен permanent redirect с example.com/user на user.example.com/
                                  Дело в том, что у на нашем сайте выносить свой аккаунт на поддомен могут только платные пользователи, поэтому бесплатным доступен адрес example.com/user, а платные могут выбрать себе урл и получить адрес user.example.com/ с перманентным редиректом со старого адреса, чтобы сохранить все страницы в поиске.

                                  Ссылки не волнуют, они генерируются автоматически, и являются не относительными, а абсолютными.

                                Only users with full accounts can post comments. Log in, please.