Когда трафик растёт, полные TLS-рукопожатия начинают ощутимо нагружать CPU и увеличивать время установления соединений. Возобновление сессии снижает стоимость handshakes: клиент и сервер договариваются однажды, а затем используют сокращённое рукопожатие. В Nginx для этого есть два механизма: session IDs (серверный кэш) и TLS session tickets (stateless-подход, где сервер шифрует секрет и отдаёт его клиенту). В кластере именно tickets позволяют добиться возобновления при переключении клиента между узлами — при условии правильного управления ключами тикетов.
Кратко о механизме: session IDs vs session tickets
Сессионный кэш (ssl_session_cache) работает в пределах процесса/хоста: сервер хранит состояние, клиент присылает идентификатор. Это быстро и предсказуемо на одном узле, но в кластере без «липкости» соединений работать не будет: другой сервер не знает об этой сессии.
TLS session tickets решают это иначе: сервер шифрует мастер-секрет с помощью внутреннего ключа (ticket key), отправляет клиенту и затем при возобновлении просто расшифровывает присланное. Если все узлы кластера знают один и тот же набор ticket keys, возобновление будет работать независимо от того, на какой сервер попадёт клиент.
Плюс: меньше CPU и задержек. Минус: появляется задача управления ключами тикетов, их ротацией и политиками хранения.
База по Nginx: включаем резюмирование и tickets
Минимальный набор директив для TLS (акцент на возобновление):
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 10m;
ssl_session_tickets on;
Кэш и таймаут работают для механизма session IDs (актуально для TLS 1.2). Директива ssl_session_tickets включает или выключает tickets. По умолчанию tickets включены, но без явного управления ключами у вас не будет единых тикетов на кластер и не будет контролируемой ротации.
Если вы только внедряете HTTPS, проверьте срок действия и цепочку доверия ваших сертификатов. Подойдут как DV, так и EV — ориентируйтесь на требования проекта. Для удобства оформления и автообновления используйте SSL-сертификаты. При миграции на HTTPS пригодится разбор практик HSTS и 301: смотрите материал про миграцию на HTTPS и HSTS.
Ключи TLS session tickets для TLS 1.2
Для TLS 1.2 Nginx позволяет задать собственные ключи тикетов через ssl_session_ticket_key. Можно указать несколько файлов: первый используется для шифрования новых тикетов, все перечисленные — для расшифрования уже выданных. Это и есть механизм безопасной ротации.
# Активный ключ (первый) + два старых для совместимости
ssl_session_ticket_key /etc/nginx/tickets/ticket-2025-10-01.key;
ssl_session_ticket_key /etc/nginx/tickets/ticket-2025-09-01.key;
ssl_session_ticket_key /etc/nginx/tickets/ticket-2025-08-01.key;
Файл ключа — это бинарник фиксированного размера, генерируемый криптографически стойкой случайностью. Типичная практика — 48 байт. Пример генерации и базовой гигиены прав:
mkdir -p /etc/nginx/tickets
openssl rand 48 > /etc/nginx/tickets/ticket-2025-10-01.key
chown root:root /etc/nginx/tickets/ticket-*.key
chmod 0400 /etc/nginx/tickets/ticket-*.key
nginx -t
systemctl reload nginx
Ротация ключей: безопасный конвейер
Цель ротации — регулярно менять ключ шифрования новых тикетов, сохраняя возможность расшифровать уже выданные. Надёжная схема выглядит так:
- Раз в N дней генерируйте новый ключ и добавляйте его первым в конфиг.
- Сохраняйте 2–3 предыдущих ключа для постепенного «сгорания» старых тикетов.
- Делайте
nginx -tи мягкийreloadна всех узлах кластера. - Удаляйте ключи старше оговорённого срока хранения.
Пример простого скрипта ротации (под cron или systemd timer):
#!/bin/sh
set -eu
DIR=/etc/nginx/tickets
DATE=$(date +%Y-%m-%d)
NEW=$DIR/ticket-$DATE.key
openssl rand 48 > "$NEW"
chmod 0400 "$NEW"
chown root:root "$NEW"
# Пересобираем фрагмент конфига с тремя последними ключами
ls -1t $DIR/ticket-*.key | head -n 3 | awk '{print "ssl_session_ticket_key " $0 ";"}' > /etc/nginx/conf.d/tickets.conf
# Убедитесь, что основной конфиг подключает этот include:
# include /etc/nginx/conf.d/tickets.conf;
nginx -t && systemctl reload nginx
# Удаляем ключи старше трёх последних
ls -1t $DIR/ticket-*.key | tail -n +4 | xargs -r rm -f
На кластере важно обеспечить одинаковый набор ключей на всех узлах до перезагрузки Nginx. Это решается распространением ключа через ваш конфиг-менеджер и «параллельным» reload с минимальным окном рассинхронизации. Если поднимаете фронты на собственных серверах, пригодится VDS, а для выбора панели администрирования — обзор панелей для VDS.
Единые тикеты на кластер (TLS 1.2)
Чтобы клиент мог возобновить сессию, попав на любой из узлов, все они должны уметь расшифровать его ticket. Для TLS 1.2 это достигается так:
- Генерируете ключ на одном контролируемом узле.
- Раскладываете ключи на все фронты в одинаковые пути.
- Обновляете конфиги так, чтобы порядок ключей совпадал (первый — текущий, далее — предыдущие).
- Одновременно делаете
reload(или быстрый последовательный «роллинг» на узлах под балансировщиком).
Практически: держите отдельный include-файл с ключами, а в основной конфигурации указывайте только include этого фрагмента. Тогда при ротации вы меняете один небольшой файл и безопасно делаете reload.
TLS 1.3: нюансы тикетов и ограничения
В TLS 1.3 возобновление работает только через PSK/tickets; server-side кэш (session IDs) не участвует. Ключевой момент: в стандартной сборке Nginx детали генерации и ротации ключей TLS 1.3 управляются криптобиблиотекой и процессом, а не директивой ssl_session_ticket_key. Это означает, что явные файлы ключей, общие на кластер, вы не зададите; ключи генерируются и ротируются внутри процесса, межузлового обмена «из коробки» нет, а перезагрузка может инвалидировать ранее выданные тикеты.
Отсюда варианты в кластере:
- Принять, что межузловое resumption в TLS 1.3 может не работать, и включить sticky на балансировщике (L4/L7 по IP, cookie или на уровне TCP).
- Оставить tickets для TLS 1.2 с едиными ключами на кластер, а в TLS 1.3 отключить tickets, если важна предсказуемость (цена — больше полных рукопожатий).
- Терминировать TLS на слое, который умеет общий пул ключей TLS 1.3 (вне рамок стандартной конфигурации Nginx).

Безопасность: что учесть при проектировании
- Минимизируйте срок жизни ключей тикетов. Частая ротация уменьшает ущерб в случае компрометации.
- Храните ключи только на фронт-узлах, шифруйте бэкапы, используйте сильную энтропию при генерации.
- Ограничьте доступ на чтение ключей до root и master-процесса Nginx, исключите попадание в репозитории, CI-логи и артефакты.
- Логируйте признак резюмирования, чтобы видеть долю повторных рукопожатий и влияние ротаций.
- Отключайте tickets там, где этого требуют политики (например, для упрощения соответствия внутренним стандартам).
- Не храните десятки старых ключей: это увеличивает окно, в котором украденный ключ будет полезен атакующему; достаточно 2–3 поколений.
Производительность: ожидания и метрики
Резюмирование сокращает CPU-время и уменьшает латентность первого запроса после установления TCP/TLS. В метриках это проявляется как рост доли abbreviated handshake, снижение средней нагрузки на ядра шифрования и уменьшение P95 времени рукопожатия. Для TLS 1.3 выигрыш особенно заметен при большом количестве коротких соединений.
Отслеживайте:
- Долю резюмирования в логах Nginx (см. ниже, переменная
$ssl_session_reused). - CPU-профили по воркерам Nginx во время пиков.
- Ошибки TLS при ротации (
error.log, причина отказа возобновления).

Наблюдаемость и логи
Чтобы видеть факт возобновления в логах, добавьте переменную $ssl_session_reused в формат:
log_format main_ext '$remote_addr $host $request $status $ssl_protocol $ssl_cipher $ssl_session_reused';
access_log /var/log/nginx/access.log main_ext;
Значение показывает r для возобновлённых сессий и . для новых. Ещё полезны $ssl_protocol и $ssl_cipher, чтобы видеть долю TLS 1.2/1.3 и используемые шифросuites.
Тестирование: как проверить resumption
Проверка на TLS 1.2 с помощью openssl:
# Первый коннект, сохраняем сессию
openssl s_client -connect your.domain:443 -tls1_2 -quiet -sess_out sess.pem < /dev/null
# Повторный коннект с реюзом
openssl s_client -connect your.domain:443 -tls1_2 -quiet -sess_in sess.pem -reconnect < /dev/null
На TLS 1.3 можно аналогично проверить повторное соединение. Если вы перезагрузите узлы или попадёте на другой сервер без общих ключей TLS 1.3, резюмирование может не сработать — это ожидаемо.
Частые ошибки и как их избежать
- Один ключ «навсегда». Ротация не проводится месяцами. Итог — высокий риск при компрометации и отсутствие управляемого цикла жизни.
- Ротация без сохранения старых ключей. Клиенты массово теряют возобновление до выпуска новых тикетов.
- Непоследовательный порядок ключей на узлах. На одном узле новый ключ первый, на другом — старый. Часть клиентов не сможет расшифровать тикеты.
- Ошибки прав. Ключи читаемы лишними процессами или недоступны Nginx — получите ошибки при старте/перезагрузке.
- Смешение ожиданий TLS 1.2 и TLS 1.3. Для TLS 1.3 нельзя управлять ключами как в TLS 1.2; учитывайте это в стратегии кластера.
Практические пресеты и рекомендации
- Малый сайт на одном узле: Оставьте tickets включёнными, для TLS 1.2 добавьте явный ключ и ротацию раз в 30 дней. Для TLS 1.3 живите с автоматическими ключами.
- Кластер под L7-балансировщиком без sticky: Для TLS 1.2 — общий пул ключей и ротация. Для TLS 1.3 — либо sticky, либо примите отсутствие межузлового resumption, либо отключите tickets, если политика так требует.
- Сегменты с повышенными требованиями: Отключайте tickets, если политика запрещает stateless resumption; полагайтесь на session IDs в пределах узла или на sticky.
Итог
TLS session tickets — мощный рычаг производительности в Nginx, особенно в кластере. Для TLS 1.2 всё прозрачно: задаём явные ключи, аккуратно ротируем, синхронизируем между узлами и получаем стабильное межузловое возобновление. Для TLS 1.3 учитывайте ограничения библиотеки: либо sticky, либо примите, что межузловое resumption не гарантируется, либо отключите tickets там, где это оправдано. Стратегия с регулярной ротацией, строгими правами на ключи и наблюдаемостью через логи позволяет совместить безопасность и производительность.


