HTTPS и TLS-терминация на Nginx

Nginx снимает с приложения всю тяжесть шифрования: клиент общается с ним по HTTPS, а внутрь, к бэкенду, трафик идёт уже расшифрованным.
«TLS-терминация — это когда Nginx распаковывает шифрование на входе, и бэкенду не нужно ничего знать про сертификаты.»

HTTPS — обязательный стандарт современного веба. Шифрование защищает данные пользователя в пути. Заниматься им должен Nginx, а не приложение: это и быстрее, и проще в управлении сертификатами. Подход называется TLS-терминацией.

Базовый HTTPS-сервер

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

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

    location / {
        proxy_pass http://127.0.0.1:8000;   # к бэкенду — по http
        proxy_set_header X-Forwarded-Proto https;
    }
}

# отдельный server для редиректа http -> https
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

listen 443 ssl включает HTTPS. Два файла — цепочка сертификата (fullchain.pem) и приватный ключ (privkey.pem). Второй server на порту 80 жёстко перенаправляет всех на https.

Схема TLS-терминации

   Клиент            Nginx (:443)          Бэкенд (:8000)
     |                  |                      |
     |== HTTPS (шифр)=> |                      |
     |                  |-- расшифровал        |
     |                  |   http (открыто) --->|
     |                  |  <-- http -----------|
     |<= HTTPS (шифр)== |                      |
     |                  |                      |

Снаружи — зашифрованный канал, внутри (Nginx-бэкенд, обычно по локальной сети или localhost) — открытый http. Бэкенд узнаёт, что исходный запрос был по https, из заголовка X-Forwarded-Proto — иначе он может строить http-ссылки или зациклить редиректы.

Как работает под капотом

При подключении происходит TLS-рукопожатие: клиент и Nginx договариваются о версии протокола и наборе шифров, Nginx предъявляет сертификат, стороны вырабатывают общий сеансовый ключ. Дальше весь трафик шифруется этим ключом. Самая дорогая часть — асимметричная криптография в начале рукопожатия; поэтому Nginx кеширует TLS-сессии и держит keepalive, чтобы не повторять рукопожатие на каждый запрос. Бэкенду шифрование не видно вовсе — Nginx сделал всю работу.

Частые ошибки

  • Указать cert.pem вместо fullchain.pem. Без промежуточных сертификатов часть клиентов не построит цепочку доверия и увидит ошибку.
  • Забыть X-Forwarded-Proto. Приложение решит, что запрос по http, и устроит цикл редиректов или смешанный контент.
  • Оставить http без редиректа. Часть пользователей останется на незащищённой версии.
  • Приватный ключ с открытыми правами. privkey.pem должен быть доступен только root.

Best practices

  • Всегда fullchain.pem (цепочка), а не голый сертификат.
  • Жёсткий редирект 301 с http на https отдельным server-блоком.
  • Передавай X-Forwarded-Proto https на бэкенд.
  • Права на приватный ключ — строго 600, владелец root.

Терминация против сквозного шифрования

TLS-терминация на Nginx — самый распространённый подход, но важно понимать его компромисс и альтернативы. При терминации трафик между Nginx и бэкендом идёт незашифрованным (http). Если они на одной машине через localhost — это абсолютно безопасно, никто посторонний этот трафик не увидит. Если бэкенд в той же защищённой внутренней сети — тоже обычно приемлемо. Преимущества огромны: сертификат живёт в одном месте, бэкенду вообще не нужно знать про шифрование, и Nginx может смотреть внутрь запроса для маршрутизации, кеширования и сжатия.

Но бывают сценарии со строгими требованиями (финансы, медицина, комплаенс), где недопустим даже незашифрованный участок во внутренней сети. Тогда применяют TLS-сквозь (re-encryption): Nginx терминирует внешний TLS, а к бэкенду подключается уже по новому TLS-соединению через proxy_pass https://... с проверкой сертификата бэкенда. Есть и крайний вариант — TLS passthrough, когда Nginx вообще не расшифровывает трафик, а пробрасывает его на бэкенд как есть (по SNI, средствами stream-модуля); но тогда теряются кеширование и разбор HTTP. Для большинства проектов простая терминация — правильный выбор по умолчанию, а более сложные схемы подключают осознанно, когда того требует модель угроз. Понимание этих вариантов помогает не переусложнять там, где достаточно localhost-проксирования, и не недооценить требования там, где они реально строгие.

Итоги

Nginx терминирует TLS: снаружи HTTPS, к бэкенду — http, плюс заголовок X-Forwarded-Proto. Нужны fullchain.pem и приватный ключ, редирект с http на https и аккуратные права на ключ. Дальше получим бесплатные сертификаты автоматически и закрутим TLS до оценки A+.

Проверьте себя
1. Что такое TLS-терминация на Nginx?
AЗапрет HTTPS
BNginx расшифровывает входящий HTTPS, а к бэкенду передаёт запрос по http, сняв с него работу с сертификатами
CСжатие сертификата
DБалансировка по IP
2. Зачем при HTTPS-проксировании передавать X-Forwarded-Proto https?
AДля сжатия
BЧтобы бэкенд знал, что исходный запрос был по https, и не строил http-ссылки и не зацикливал редиректы
CДля балансировки
DЧтобы открыть порт 80