Удалите «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.