Nginx: редирект с http на https и с www на без www

Категория: Nginx · Язык: Bash · Обновлено:

Чтобы перенаправить http на https в Nginx, добавьте отдельный server на порт 80 с директивой return 301 https://$host$request_uri;. Она сохраняет домен, путь и параметры запроса, поэтому старые ссылки продолжают работать.

Базовая конфигурация

Текст
# http -> https
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

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

# основной сайт
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    root /var/www/example.com;
    index index.html;
}

После изменений проверьте синтаксис и перезагрузите конфигурацию без остановки сервера:

Bash
sudo nginx -t
sudo systemctl reload nginx

Редирект с www на домен без www

Для адреса с www на порту 443 тоже нужен сертификат, который покрывает оба имени, иначе браузер покажет предупреждение до выполнения редиректа.

Текст
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

Запрос на http://www.example.com при такой схеме пройдёт два редиректа: сначала на https://www.example.com, затем на https://example.com. Чтобы сократить цепочку до одного шага, в блоке порта 80 укажите основной домен явно: return 301 https://example.com$request_uri;.

Если нужно наоборот, используйте основным адрес с www: поменяйте имена местами в обоих блоках. Какой вариант выбрать, неважно, но выберите один и придерживайтесь его: поисковикам нужен единый основной адрес.

Проверка редиректа

Bash
curl -I http://example.com/page?x=1
# HTTP/1.1 301 Moved Permanently
# Location: https://example.com/page?x=1

В выводе проверьте код ответа 301 и заголовок Location: в нём должны сохраниться путь и параметры.

Редирект и Certbot (Let's Encrypt)

Если сертификат выпускается через проверку по HTTP (webroot), запросы к /.well-known/acme-challenge/ не должны уходить на https. Добавьте исключение в блок порта 80:

Текст
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

При запуске certbot --nginx нужные строки в конфигурацию добавляются автоматически, поэтому ручное исключение требуется в основном для режима certbot certonly --webroot.

Какой код редиректа выбрать

Код

Название

Когда использовать

301

Moved Permanently

Постоянный редирект для сайтов и страниц. Метод POST браузеры могут заменить на GET

308

Permanent Redirect

Постоянный редирект, который сохраняет метод запроса. Подходит для API с POST и PUT

302

Found

Временный редирект. Для перехода на https не используйте

Бесконечный редирект за прокси или CDN

Если https завершается на балансировщике или в CDN, а Nginx получает обычный http, редирект по порту 80 зациклится. В таком случае ориентируйтесь на заголовок, который передаёт прокси:

Текст
server {
    listen 80;
    server_name example.com;

    if ($http_x_forwarded_proto = "http") {
        return 301 https://$host$request_uri;
    }

    # дальше обычная обработка запросов
}

Убедитесь, что прокси действительно передаёт X-Forwarded-Proto, и что клиенты не могут подставить его сами в обход прокси.

Частые вопросы

В чём разница между $host, $server_name и $request_uri?

$host — имя хоста из запроса, $server_name — первое имя из директивы server_name, а $request_uri — путь вместе с параметрами в исходном виде. Для редиректа с http на https обычно берут $host$request_uri.

Почему браузер показывает «слишком много перенаправлений»?

Обычно причина в циклической цепочке: Nginx перенаправляет на https, а прокси или CDN снова отправляет запрос по http. Проверьте режим SSL в CDN и условие по X-Forwarded-Proto.

Нужно ли включать HSTS после редиректа?

Заголовок Strict-Transport-Security заставляет браузер сразу открывать сайт по https. Добавляйте его, когда убедились, что https стабильно работает на всех поддоменах: отменить его для уже запомнивших браузеров быстро не получится.