// Engineering Log

HTTP, HTTPS и TLS: Часть 7 — Переход на HTTPS: редиректы, HSTS, HTTPS-First и заголовок Upgrade

Опубликовано 09.10.2026

// Быстрый маршрут

Эта статья относится к теме Серверы и инфраструктура.

Пользователь набирает в адресной строке example.ru без схемы, переходит по старой ссылке http://… или открывает закладку десятилетней давности. Во всех этих случаях первый запрос может уйти по открытому HTTP, и задача владельца сайта — чтобы дальше всё шло по HTTPS, а в идеале — чтобы открытого запроса не было вовсе. Для этого существует несколько механизмов, которые работают на разных этапах: редирект на сервере, HSTS, встроенный список preload, режим HTTPS-First в браузере, DNS-запись типа HTTPS. Отдельно разобран заголовок Upgrade, с которым часто путают «HTTPS upgrade»: он переключает соединение на другой протокол, например WebSocket.

Редирект с HTTP на HTTPS

Базовый уровень — сервер на порту 80 отвечает перенаправлением на тот же адрес по HTTPS:

nginx
server {
    listen 80;
    listen [::]:80;
    server_name example.ru www.example.ru;

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;   # проверка Let's Encrypt HTTP-01
    }

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

Что важно:

  • 301 или 308. Оба — постоянные редиректы, поисковики переносят на новый адрес вес страницы. 308 сохраняет метод и тело запроса; при 301 браузер превращает POST в GET. Для сайта разница незаметна, для API, куда клиенты по ошибке шлют POST на http://, лучше 308.
  • Сохраняйте путь. $request_uri содержит путь и параметры. Редирект всех страниц на главную ломает ссылки и поисковую выдачу.
  • Один шаг. Цепочка http://example.ru → https://example.ru → https://www.example.ru — два лишних круга. Сразу направляйте на итоговый адрес.
  • Let’s Encrypt HTTP-01 следует редиректам, поэтому отдельный location для acme-challenge не обязателен, но избавляет от сюрпризов при нестандартных настройках.

Слабое место редиректа: первый запрос всё равно идёт открыто. Злоумышленник в той же сети (например, в публичном Wi-Fi) может перехватить его и не пускать пользователя на HTTPS, отдавая ему сайт по HTTP через себя. Этот приём называется SSL stripping, и защищает от него HSTS.

HSTS

HSTS (HTTP Strict Transport Security, RFC 6797) — заголовок ответа, которым сайт сообщает браузеру: «в течение указанного срока обращайся ко мне только по HTTPS».

Strict-Transport-Security: max-age=31536000; includeSubDomains
  • max-age — срок в секундах (31536000 — один год). Каждый ответ с заголовком продлевает срок;
  • includeSubDomains — правило распространяется на все поддомены;
  • preload — согласие на включение в список preload (ниже).

Получив заголовок, браузер:

  1. сам переписывает любые http:// ссылки на этот сайт в https:// ещё до отправки запроса — редирект сервера больше не нужен (в инструментах разработчика это видно как 307 Internal Redirect);
  2. не даёт пользователю пропустить ошибку сертификата: кнопки «Всё равно перейти» нет.

Правила:

  • браузер принимает заголовок только по HTTPS; в ответе по HTTP он игнорируется;
  • начинайте с небольшого max-age (300, потом 86400, потом неделя) и увеличивайте, когда убедитесь, что всё работает по HTTPS. Отменить HSTS быстро нельзя: браузеры, запомнившие заголовок, будут требовать HTTPS до конца срока;
  • includeSubDomains проверяйте особенно внимательно: если у вас есть intranet.example.ru или printer.example.ru без HTTPS, после этого они перестанут открываться у всех, кто посетил основной сайт.

В Nginx:

nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Список HSTS preload

HSTS начинает действовать после первого визита по HTTPS. Первый визит остаётся незащищённым. Чтобы закрыть и его, существует список preload — перечень доменов, который встроен в Chrome, Firefox, Safari и Edge. Для доменов из списка браузер использует только HTTPS с самого первого запроса.

Требования для включения (hstspreload.org):

  1. действующий сертификат;
  2. редирект с HTTP на HTTPS на том же имени хоста;
  3. все поддомены доступны по HTTPS;
  4. заголовок на основном домене: max-age не меньше 31536000, includeSubDomains и preload.

Подать заявку можно на hstspreload.org, попадание в стабильные версии браузеров занимает недели. Удаление из списка занимает месяцы и не действует на старые версии браузеров, поэтому preload — решение необратимое на практике. Некоторые доменные зоны включены в список целиком: все сайты в .dev, .app, .page работают только по HTTPS.

HTTPS-First в браузерах

Браузеры не ждут, пока сайт настроит HSTS, и сами пробуют HTTPS.

  • Chrome с версии 115 (2023) при переходе на http:// сначала пробует HTTPS и при неудаче откатывается на HTTP (HTTPS-Upgrades). Настройка «Всегда использовать безопасные подключения» (Always Use Secure Connections) дополнительно предупреждает перед открытием сайта по HTTP. Google объявил, что в Chrome 154 (октябрь 2026 года) эта настройка будет включена по умолчанию: браузер будет спрашивать пользователя перед первым заходом на публичный сайт без HTTPS и не станет повторять предупреждение для сайтов, которые пользователь посещает регулярно. Для пользователей с усиленным режимом Safe Browsing она включена с апреля 2026 года.
  • Firefox с версии 136 (март 2025) работает в режиме HTTPS-First по умолчанию: пробует HTTPS и откатывается на HTTP, если сайт не отвечает. Отдельно есть более строгий режим HTTPS-Only.
  • Safari также пробует HTTPS для адресов без схемы.

Что это значит для владельца сайта: если по HTTPS на вашем домене открывается что-то другое, чем по HTTP (заглушка хостинга, чужой сайт на том же IP, ошибка сертификата), пользователи всё чаще будут видеть именно это. HTTPS должен работать на всех именах, которые вы публикуете, а сайты без HTTPS будут сопровождаться предупреждением.

Upgrade-Insecure-Requests и смешанное содержимое

Смешанное содержимое (mixed content) — страница по HTTPS, которая загружает скрипты, стили или картинки по HTTP. Браузеры блокируют такие скрипты, стили и фреймы, а картинки, видео и аудио пытаются загрузить по HTTPS, и если не получается — не показывают.

Две похожие по названию вещи:

  • Заголовок запроса Upgrade-Insecure-Requests: 1. Браузер отправляет его при переходе на страницу, сообщая: «я предпочитаю HTTPS». Сервер может ответить на него редиректом на HTTPS. На практике для этого достаточно обычного редиректа, заголовок используется редко.

  • Директива CSP upgrade-insecure-requests. Заголовок ответа, который велит браузеру переписать все http:// ресурсы на странице в https:// перед загрузкой:

    Content-Security-Policy: upgrade-insecure-requests

    Полезно при переносе старого сайта, в базе которого тысячи ссылок на картинки с http://: пока ссылки не исправлены, директива убирает предупреждения. Ресурсы должны быть доступны по HTTPS — директива не создаёт HTTPS там, где его нет.

DNS-запись HTTPS

Запись типа HTTPS (RFC 9460) сообщает клиенту параметры подключения до первого запроса:

example.ru.  3600  IN  HTTPS  1 . alpn="h3,h2" ipv4hint=192.0.2.10
  • само наличие записи говорит браузеру, что сайт доступен по HTTPS: Chrome и Firefox в этом случае сразу идут на https://, минуя открытый HTTP, так же как при HSTS;
  • alpn — какие протоколы поддерживаются; с h3 браузер может сразу подключаться по HTTP/3;
  • ech — ключ для Encrypted Client Hello;
  • ipv4hint, ipv6hint — адреса, чтобы не ждать ответа на A/AAAA.

Запись поддерживают Cloudflare (создаёт её автоматически) и часть DNS-хостингов. Проверка: dig HTTPS example.ru.

Заголовок Upgrade: переключение протокола

Upgrade — механизм HTTP/1.1 для перехода с HTTP на другой протокол внутри того же TCP-соединения. Клиент предлагает, сервер соглашается ответом 101 Switching Protocols, и дальше по соединению идёт уже новый протокол.

WebSocket (RFC 6455) — главный пример. Чем WebSocket отличается от SSE и long polling и когда какой выбирать — в статье «WebSockets, Long Polling и SSE». Здесь — как выглядит сам переход:

GET /ws HTTP/1.1
Host: example.ru
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Адрес wss:// — это WebSocket поверх TLS, то есть сначала устанавливается HTTPS, а внутри него — Upgrade.

Upgrade и Connection — заголовки одного участка пути, и прокси их не передаёт дальше. Поэтому для WebSocket за Nginx нужна явная настройка:

nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    location /ws/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;   # по умолчанию 60 с — простаивающее соединение закроется
    }
}

Без этих строк клиент получит 400 или 426, а в журнале приложения будет обычный GET без Upgrade.

В HTTP/2 и HTTP/3 заголовка Upgrade нет. WebSocket в них открывается через расширенный метод CONNECT (RFC 8441 для HTTP/2, RFC 9220 для HTTP/3) с псевдозаголовком :protocol: websocket. Браузеры и серверы договариваются об этом сами; Nginx с приложением по-прежнему общается по HTTP/1.1.

Другие применения Upgrade:

  • Upgrade: h2c — переход на HTTP/2 без шифрования. Браузеры его никогда не поддерживали, а RFC 9113 (2022) признал этот способ устаревшим. Прокси, которые пропускают Upgrade: h2c к приложению, иногда позволяют обойти их правила доступа (h2c smuggling) — такие заголовки лучше отбрасывать на входе;
  • Upgrade: TLS/1.0 — переход на TLS внутри HTTP-соединения (RFC 2817). В вебе не прижился; вместо него используется отдельный порт 443.

Если сервер требует перехода на другой протокол, он отвечает кодом 426 Upgrade Required с заголовком Upgrade, где указан нужный протокол.

Порядок перевода сайта на HTTPS

  1. Выпустить сертификат на все используемые имена, включая www, и настроить автоматическое продление.
  2. Проверить, что сайт полностью работает по HTTPS: внутренние ссылки, картинки, формы, сторонние виджеты. Для старых ссылок в содержимом — CSP upgrade-insecure-requests.
  3. Включить редирект 301 с HTTP на HTTPS, сохраняя путь.
  4. Указать https:// в канонических адресах, карте сайта, в Яндекс Вебмастере и Google Search Console.
  5. Включить HSTS с коротким max-age, через несколько недель — год и includeSubDomains.
  6. Если уверены во всех поддоменах — подать домен в список preload.

Типичные ошибки

  • Бесконечный редирект: приложение за прокси не видит X-Forwarded-Proto: https и снова отправляет на HTTPS; то же бывает с Cloudflare в режиме Flexible.
  • HSTS с includeSubDomains на основном домене при поддоменах без HTTPS — внутренние сервисы перестают открываться.
  • Заголовок HSTS отдаётся по HTTP — браузер его игнорирует, защиты нет.
  • Редирект на главную вместо той же страницы.
  • WebSocket за Nginx без proxy_http_version 1.1 и заголовков Upgrade/Connection, либо с proxy_read_timeout по умолчанию — соединения рвутся раз в минуту.

// Похожая задача

Если у вас похожая ситуация

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

Тема статьи

Серверы и инфраструктура

VPS, Linux, веб-стек, миграции, хостинг, базы данных и базовая эксплуатация.

Часто с этим приходят

  • Перенести сайт или сервис на новый сервер
  • Настроить Linux, Nginx, базу данных и бэкапы
  • Разобраться, почему всё работает нестабильно

// Следующий шаг

Если вам нужна не только статья, а помощь по этой теме, удобнее сразу перейти в услугу. Главная и подборка материалов остаются рядом.

Открыть услуги

// Reviews

Отзывы по теме

Спасибо Михаилу за отзывчивость. Созвонились, объяснил как сделать самому. Обращаюсь уже второй раз, все супер и оперативно.

Спасибо Михаилу за отзывчивость. Созвонились, объяснил как сделать самому. Обращаюсь уже второй раз, все супер и оперативно.

kireevk

kireevk

Консультация по nginx proxy manager и portainer

25.02.2025 · ★ 5/5

Освоившийся покупатель
apande

apande

Настройка nginx и opencart

07.09.2024 · ★ 5/5

Очень мощный покупатель
kireevk

kireevk

Диагностика Nginx Proxy Manager в docker контейнере и решение проблемы

15.03.2024 · ★ 5/5

Освоившийся покупатель

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Написать в Telegram

Отвечаю в течение рабочего дня (03:00–13:00 GMT)

Или оставьте заявку здесь:

Подтвердите, что вы не бот.

Написать и получить быстрый ответ