Nginx переписывает домен без префикса www в домен с префиксом www

Я вижу, что в документации по Nginx HttpRewriteModule есть пример для перезаписи домена с префиксом www в домен без префикса www:

if ($host ~* www\.(.*)) {
  set $host_without_www $1;
  rewrite ^(.*)$ http://$host_without_www$1 permanent; # $1 contains '/foo', not 'www.mydomain.com/foo'
}

Как я могу сделать наоборот - переписать домен без префикса www в домен с префиксом www? Я подумал, что мог бы сделать что-то вроде следующего, но Nginx не нравится вложенное выражение if.

if ($host !~* ^www\.) {                       # check if host doesn't start with www.
    if ($host ~* ([a-z0-9]+\.[a-z0-9]+)) {    # check host is of the form xxx.xxx (i.e. no subdomain)
        set $host_with_www www.$1;
        rewrite ^(.*)$ http://$host_with_www$1 permanent;
    }
}

Также я хотел, чтобы это работало для любого доменного имени без явного указания Nginx переписать domain1.com -> www.domain1.com, domain2.com -> www.domain2.com и т. Д., Так как у меня есть большое количество доменов для перезаписи,

5 ответов

Решение

Ну, я думаю, мне действительно не нужен внешний оператор "if", так как я все равно проверяю только домены вида xxx.xxx. Следующее работает для меня, хотя это не надежно. Дайте мне знать, если есть лучшее решение.

    if ($host ~* ^([a-z0-9\-]+\.(com|net|org))$) {
        set $host_with_www www.$1;
        rewrite ^(.*)$ http://$host_with_www$1 permanent;
    }

Редактировать: добавлен дефис к регулярному выражению, так как это допустимый символ в имени хоста.

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

Лучшим решением было бы использование нескольких директив сервера.

server {
        listen 80;
        server_name website.com;
        return 301 $scheme://www.website.com$request_uri;
}

server {
        listen 80;
        server_name www.website.com;
        ... 
}

Если вы пытаетесь обслуживать сайт с поддержкой SSL (HTTPS), у вас есть более или менее три разных варианта.

  1. Настройте несколько IP-адресов, чтобы каждая серверная директива прослушивала свой собственный IP-адрес (или разные порты, если это вариант для вас). Для этого варианта требуются сертификаты SSL как для веб-сайта website.com, так и для веб-сайта www.website.com, поэтому у вас есть сертификат подстановочного знака, сертификат UNI (несколько доменов) или просто два разных сертификата.
  2. Сделайте переписать в приложении.
  3. Используйте страшных if директивы.

Существует также возможность использовать SNI, но я не уверен, что это полностью поддерживается на данный момент.

if ($host !~* ^www\.) {
    rewrite ^(.*)$ http://www.$host$1 permanent;
}
if ($host ~* ^[^.]+\.[^.]+$) {
    rewrite ^(.*)$ http://www.$host$1 permanent;
}

Получить действительные имена хостов можно только потому, что запрос никогда не поступит на ваш сервер, поэтому вам не нужно создавать собственную логику проверки.

Документация nginx предостерегает от использования if для перезаписи. Пожалуйста, смотрите ссылку здесь: http://wiki.nginx.org/Pitfalls

HTTP & HTTPS без условий:

server {
    listen          80;
    listen          443;
    server_name     website.com;

    return 301 $scheme://www.website.com$request_uri;
}
server {
    listen          80;
    listen          443 default_server ssl;
    server_name     www.website.com;

    # Your config goes here #
}

Решение для нескольких доменов, работающее на nginx 1.17 для меня:

server {
        listen                                80;
        server_name                           .example.com;

        set $host_with_www                 $host;

        if ($host !~* www\.(.*)) {
            set $host_with_www www.$host;
        }

        return                                301 https://$host_with_www$request_uri;
}

В этом примере конфигурации дополнительно переписывает HTTP на HTTPS, если вы не хотите переписывать - замените https:// на http:// в return строка.

Если хотите сохранить протокол - используйте $scheme переменная.

Другие вопросы по тегам