Удалите «www» и перенаправьте на «https» с помощью nginx


57

Я хочу создать правило в nginx, которое делает две вещи:

  1. Удаляет "www." из запроса URI
  2. Перенаправляет на «https», если URI запроса - «http»

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

Он должен обрабатывать все эти случаи:

1. http://www.example.com/path
2. https://www.example.com/path
3. http://example.com/path
4. https://example.com/path

Все они должны заканчиваться на https://example.com/path (# 4) без зацикливания. Есть идеи?


Я просто перенаправил www.mydomain.com на mydomain.com на уровне DNS и добавил 301 для не-https в https в nginx. Похоже , что должно быть хорошо ¯ \ _ (ツ) _ / ¯
— jonathanbell

Ответы:


94

Лучший способ сделать это - использовать три серверных блока: один для перенаправления http на https, один для перенаправления http-имени https на no-www, а второй для фактической обработки запросов. Причина использования дополнительных серверных блоков вместо ifs заключается в том, что выбор сервера выполняется с использованием хеш-таблицы и выполняется очень быстро. Использование уровня сервера if означает, что if запускается для каждого запроса, что бесполезно. Кроме того, захват запрошенного URI в перезаписи является расточительным, так как nginx уже имеет эту информацию в переменных $ uri и $ request_uri (без и со строкой запроса, соответственно).

server {
    server_name www.example.com example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name example.com;

    <locations for processing requests>
}

2
Нужен ли средний блок? Разве первый блок уже не переписывается с www на non-www?
— pbreitenbach

3
Первый блок обрабатывает только http. Средний блок необходим для перенаправления запросов https с https: // www.example.com/ на https: // example.com/. (Извините за лишние пробелы, я не могу заставить это показать https иначе)
— kolbyjack

1
просто небольшая заметка о форматировании - если вы хотите избежать создания ссылки, вы можете поместить текст комментария в кавычки `, тот, что под тильдой. Это будет отображаться как:https://example.com/
— Циклоп

9
второй блок также нуждается в сертификате.
— Рика,

3
Пробуя этот ответ, я столкнулся с другой проблемой. Думал я мог переадресацию 301 от www.sub.example.comк sub.example.comи затем только получить сертификат SSL для sub.example.comТеперь я знаю , что проверка сертификата SSL происходит до 301 редиректов, поэтому она не может работать. Более подробное объяснение здесь: serverfault.com/a/358625/144811
— Gruzzles

11

Это работает для меня:

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

server {
    listen              443 ssl;
    server_name         www.yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    return              301 https://yourdomain.com$request_uri;
}

server {
    listen              443 ssl;
    server_name         yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

    # do the proper handling of the request
}

Имейте в виду, что оба yourdomain.com и www.yourdomain.com должны быть в вашем SSL-сертификате. Это возможно с подстановочным сертификатом или с альтернативным именем сервера, как описано здесь . Проверьте https://www.startssl.com для хороших и бесплатных сертификатов, которые делают это. ( Edith : начиная с Chrome версии 56, сертификаты StartSL больше не будут доверять. Попробуйте вместо этого https://letsencrypt.org/ .)


Это на самом деле работает, но я подумал, что это можно сделать более понятным способом без большого количества дублирующих строк конфигурации.
— zloynemec

@zloynemec Вы можете поместить содержимое SSL в отдельный файл .conf и использовать includeправило для добавления его в оба блока сервера SSL.
— Igettäjä

Также, если вы используете cloudflare, вам нужно заплатить сертификат в 10 долларов за месяц, чтобы иметь возможность перенаправить и проксировать 2 субдомена (www + что-то). Дайте мне знать, если есть обходной путь.
— Фридо

7

Проведя так много времени с сотнями подобных случаев, я пришел к следующему фрагменту. Он короткий и легко подстраивается под что угодно.

server {
    listen 80;
    listen 443 ssl;
    server_name example.com www.example.com;
    ssl_certificate /path/to/my/certs/example.com/fullchain.pem;
    ssl_certificate_key /path/to/my/certs/example.com/privkey.pem;

    # Redirect to the correct place, if needed
    set $https_redirect 0;
    if ($server_port = 80) { set $https_redirect 1; }
    if ($host ~ '^www\.') { set $https_redirect 1; }
    if ($https_redirect = 1) {
        return 301 https://example.com$request_uri;
    }

    location / {
    # ...
}

Ох, но ifэто зло !

Да, это может быть. Но оно существует по определенной причине и не должно причинять вреда тем, кто знает, как правильно его использовать. ;)


Мне это нравится, но есть ли у вас какие-либо данные о падении производительности? Спасибо!
— Фридо

1
Честно говоря, я никогда не проверял это, но я полагаю, что вряд ли будет какое-то влияние по сравнению с отдельными правилами, поскольку эффект почти одинаков.
— Эмиллер

тест на перенаправление? это не совсем уместно, нет? (истинный вопрос, а не тролль ^^)
— Матрица

3

Я предпочитаю вернуться с кодом ответа, чтобы браузер знал, что вы перенаправляете его на другой URL.

server {
    listen   80;
    server_name  www.example.com;

    return 301 https://example.com$request_uri;
}

затем другой блок настроек сервера для https

server {
        listen   443 ssl;
        server_name  example.com;
        ...
    }

0

как насчет создания блока сервера для этой цели:

server{
    listen 80;
    server_name www.example.net example.net;
    rewrite ^(.*) https://example.net$1 permanent;
}

затем перезапустить nginx


Я получаю сообщение об ошибке «конфликтующее имя сервера» при перезапуске. Кроме того , эта команда не будет слушать порт 443 для SSL и мне нужно беспокоиться о перенаправлении https://www.example.comк https://example.comа.
— Девин

0

Я думаю, что это должно работать.

В вашем простом определении HTTP-сервера предлагается что-то вроде anthonysomerset, а именно:

rewrite ^(.*) https://example.net$1 permanent;

Затем на вашем определении сервера SSL:

if ($host ~ /^www\./) {
  rewrite ^(.*) https://example.net$1 permanent;
}

Таким образом, перенаправление должно происходить только один раз для каждого запроса, независимо от того, на какой URL-адрес изначально переходит пользователь.


Это сработало, спасибо. Я должен был изменить ваше условие, if ($host = 'www.example.com') {так как ваше регулярное выражение не работает для меня, хотя. Понятия не имею почему, как это выглядит правильно.
— Девин

Обратите внимание, что если это зло, и, как правило, лучше использовать декларативный способ.
— Блейз

0

Вот полный пример, который помог мне. Проблема заключалась в том, что у меня не было сведений о ssl ( ssl_certificateи т. Д.) В блоке перенаправления www. Не забудьте проверить свои журналы ( sudo tail -f /var/log/nginx/error.log)!

# HTTP — redirect all traffic to HTTPS
server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}

# HTTPS — redirects www to non-www
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;
    return 301 https://example.com$request_uri;
}

# HTTPS — proxy all requests to the app (port 3001)
server {
    # Enable HTTP/2
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com sub.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;

    # For LetsEncrypt:
    location ~ /.well-known {
        root /var/www/html;
        allow all;
    }

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:3001;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.