Видаліть "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, один для перенаправлення https-імені www на ім'я no-www і одного для фактичної обробки запитів. Причиною використання додаткових блоків сервера замість ifs є те, що вибір сервера здійснюється за допомогою хеш-таблиці, і це дуже швидко. Використання серверного рівня, якщо означає, що запускається if для кожного запиту, що марно. Крім того, захоплення запитуваного урі у перезаписі є марним, оскільки 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 на не www?
pbreitenbach

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

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

9
другий блок також потребує інформації про cert.
ricka

3
Спробувавши цю відповідь, я зіткнувся з іншою проблемою. Думав, що я можу 301 переадресувати з www.sub.example.comна, sub.example.comа потім отримати лише сертифікат SSL. sub.example.comТепер я знаю, що перевірка ssl cert відбувається перед перенаправленням 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, чи є приємні та безкоштовні сертифікати, які це роблять. ( Едіт : починаючи з версії Chrome 56, сертифікатам startssl більше не довіряти. Натомість спробуйте 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
Чесно кажучи, я ніколи цього не орієнтовував, але я вважаю, що навряд чи буде вплив в порівнянні з окремими правилами, оскільки ефект майже однаковий.
emyller

орієнтир щодо перенаправлення? це не справді доречно ні? (справжнє запитання, а не троль ^^)
Матриця

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.