Как работает HTTPS на сайте

Когда мы открываем сайт через https://, браузер делает гораздо больше, чем просто отправляет запрос на сервер. Он сначала проверяет, действительно ли сервер тот, за кого себя выдает, договаривается с ним о шифровании, а уже потом передает обычный HTTP-запрос.
HTTPS — это HTTP, который работает поверх TLS. То есть сама логика запроса остается знакомой:
GET / HTTP/1.1
Host: instagram.comНо перед тем как такой запрос уйдет на сервер, браузер и сервер устанавливают защищенное соединение.
Что происходит при открытии сайта?
Допустим, пользователь открывает:
https://instagram.comСначала браузер узнает IP-адрес домена через DNS. Потом подключается к серверу на порт 443.
curl -I https://instagram.comОбычно в ответ можно увидеть примерно такое:
HTTP/2 200
content-type: text/html
strict-transport-security: max-age=31536000Но до этого ответа уже прошел TLS-handshake.
Упрощенно процесс выглядит так:
Браузер: привет, я хочу подключиться по TLS
Сервер: вот мой сертификат
Браузер: проверяю, кому он выдан и можно ли ему доверять
Браузер и сервер договариваются о ключах шифрования
Дальше весь HTTP-трафик идет в зашифрованном виде
Зачем нужен сертификат?
Сертификат подтверждает, что сервер имеет право обслуживать конкретный домен. Например, сертификат для instagram.com должен быть выдан именно для instagram.com, а не для чужого сайта.
Посмотреть сертификат можно так:
openssl s_client -connect instagram.com:443 -servername instagram.comКлючевой момент здесь — servername. Он передает имя домена через SNI, потому что на одном IP может жить несколько сайтов с разными сертификатами.
Сертификат содержит:
домен, для которого он выпущен
срок действия
публичный ключ сервера
центр сертификации, который его подписал
цепочку доверия до корневого сертификата
Браузер проверяет эту цепочку. Если сертификат просрочен, выдан не тому домену или подписан неизвестным центром, пользователь увидит предупреждение.
Почему HTTPS не шифрует всё одним ключом
Есть публичный и приватный ключ. Публичный можно отдавать кому угодно, приватный должен оставаться только на сервере.
Но сам трафик обычно не шифруется постоянно асимметричным ключом, потому что это дорого по ресурсам. Вместо этого TLS использует асимметричную криптографию для безопасного обмена данными, а дальше стороны получают общий сессионный ключ.
Упрощенно:
const browser = {
supports: ["TLS 1.3", "AES-GCM", "ChaCha20-Poly1305"],
};
const server = {
certificate: "certificate-for-instagram.com",
privateKey: "server-private-key",
};
const session = {
encrypted: true,
symmetricKey: "temporary-session-key",
};
Этот symmetricKey живет только в рамках конкретной сессии. После этого запросы и ответы шифруются намного быстрее.
Как это выглядит в Nginx
На практике HTTPS часто настраивают на уровне reverse proxy. Например, Nginx принимает HTTPS-запросы, а потом проксирует их внутрь приложения.
Пример конфигурации:
server {
listen 80;
server_name instagram.com www.instagram.com;
return 301 https://instagram.com$request_uri;
}
server {
listen 443 ssl http2;
server_name instagram.com;
ssl_certificate /etc/letsencrypt/live/instagram.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/instagram.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}Здесь приложение может работать внутри сервера на обычном HTTP:
127.0.0.1:3000А внешний пользователь всё равно видит защищенное соединение:
https://instgram.comЧто делает заголовок HSTS
Есть еще полезный заголовок:
Strict-Transport-Security: max-age=31536000; includeSubDomainsОн говорит браузеру: в течение указанного времени открывай этот сайт только через HTTPS.
Это защищает от ситуации, когда пользователь случайно переходит на http://instagram.com, а злоумышленник пытается перехватить или изменить редирект.
В Nginx это можно добавить так:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;Но с HSTS лучше быть аккуратным. Если включить его с includeSubDomains, все поддомены тоже должны нормально работать по HTTPS.
Что HTTPS реально защищает
HTTPS защищает от чтения и изменения трафика между пользователем и сервером. Например, провайдер, публичный Wi-Fi или промежуточный узел не должны увидеть содержимое запросов, cookies, пароли и ответы сервера.
Но HTTPS не спасает от всего.
Если на сайте есть XSS, плохая авторизация, утечка токенов, слабые пароли или дырявая backend-логика, один сертификат проблему не решит.
Можно проверить сайт так:
curl -I https://instagram.comИ обратить внимание на такие вещи:
strict-transport-security
set-cookie: session=...; Secure; HttpOnly; SameSite=LaxДля cookies особенно важен флаг Secure:
Set-Cookie: access_token=abc123; Secure; HttpOnly; SameSite=LaxОн запрещает браузеру отправлять cookie по обычному HTTP.
Итог
HTTPS — это не просто “замочек в адресной строке”. За ним стоит целая цепочка: DNS, TCP, TLS-handshake, сертификат, проверка доверия, обмен ключами и только потом обычный HTTP-запрос.
Для разработчика важно понимать хотя бы базовую схему, потому что многие проблемы на проде выглядят примерно так:
SSL certificate problem
ERR_CERT_COMMON_NAME_INVALID
ERR_TOO_MANY_REDIRECTS
Mixed Content
Cookie was not sent because it had the Secure attributeИ почти всегда причина где-то в связке: домен, сертификат, reverse proxy, редиректы, cookies и заголовки.
А вы сталкивались с ошибками HTTPS на проде? Что было самым неприятным: сертификаты, редиректы, mixed content или cookies?
Discussion
0 comments
Log in to write, edit, and rate comments

