Обновление TLS-сертификата на действующем сайте у многих администраторов связано с тревожным ожиданием: кажется, что без остановки веб-сервера новые ключи не подхватятся, а пользователи увидят ошибки соединения. Практика устроена спокойнее. nginx рассчитан на смену сертификатов на лету, и вся операция умещается в короткий набор шагов: новые файлы ложатся рядом со старыми, конфигурация проходит проверку, службе уходит команда плавной перезагрузки. Посетители продолжают ходить по страницам, открытые соединения не обрываются, а первые новые запросы уже обслуживаются свежей цепочкой. Ниже разобрана механика процесса, включая ловушки с правами на ключ, поведение символических ссылок и порядок обновления нескольких серверов.
Механика плавной перезагрузки и разделение соединений между старыми и новыми процессами
Внутри nginx всегда работают два уровня процессов. Главный процесс трудится от имени суперпользователя, читает конфигурацию и распоряжается слушающими портами, а рабочие процессы принимают и обслуживают соединения. Сертификат и приватный ключ попадают в память в момент разбора конфигурации, поэтому простая замена файлов на диске сама по себе ничего не меняет: сервер продолжает отдавать прежнюю цепочку, пока не получит команду перечитать настройки.
Команда nginx -s reload отправляет главному процессу сигнал SIGHUP. Тот заново читает конфигурационные файлы, раскрывает все include, подгружает новые сертификаты и проверяет корректность собранной картины. Если конфигурация собралась без ошибок, главный процесс порождает новую партию рабочих процессов, которые слушают те же порты и уже держат в памяти свежие файлы. Старые рабочие получают указание больше не брать новые соединения и спокойно доработать: каждый обслуживает только те подключения, которые успел принять раньше, а после закрытия последнего завершается. Отсюда главное свойство процедуры: приём соединений не прерывается ни на мгновение, а смена сертификата растягивается на время жизни старых подключений.
У механизма есть и запасной сценарий. Если новый конфиг содержит опечатку или файлы сертификата нечитаемы, главный процесс фиксирует ошибку в журнале и продолжает работу со старыми настройками: сайт отвечает, как и отвечал. Именно поэтому плавная перезагрузка считается щадящей отправной точкой для правок конфигурации, хотя проверять её перед командой всё равно нужно.
Подготовка новых файлов сертификата и права на приватный ключ
Начинается всё с файлов. Типовой набор состоит из сертификата сервера, промежуточных сертификатов издателя, собранных в один файл цепочки, и приватного ключа. Удобная привычка раскладывать материалы по отдельным каталогам с явными именами, например /etc/nginx/ssl/shop.example.com/ с файлами fullchain.crt и server.key. Блок виртуального хоста при такой раскладке выглядит компактно:
server {
listen 443 ssl;
server_name shop.example.com;
ssl_certificate /etc/nginx/ssl/shop.example.com/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/shop.example.com/server.key;
}
Предыдущую версию материалов сохраняют рядом, переименовав каталог в shop.example.com.previous: копия занимает считанные килобайты и экономит часы при откате.
Права на приватный ключ заслуживают отдельного разговора. Владельцем назначают root, права сужают до 600. Главный процесс nginx читает ключ на старте от имени суперпользователя, поэтому рабочим процессам, трудящимся от непривилегированного пользователя, доступ к файлу не нужен: ключ уже в памяти. Если сервер настроен нестандартно и главный процесс стартует от обычного пользователя, файл ключа отдают группе этого пользователя, но расширять права шире необходимого минимума не стоит. Отдельно проверяют, что новые файлы не остались с правами 644 после копирования с другой машины: открытый ключ публикация не портит, а вот ключ в общем доступе отменяет весь смысл шифрования.
Бывает, что сертификат выпускается утилитой автоматического продления, которая раскладывает свежие версии по архивным каталогам и обновляет символические ссылки. Такой расклад совместим с плавной перезагрузкой, но у него есть особенности, о которых пойдёт речь ниже.
Проверка синтаксиса конфигурации и пары ключа с сертификатом до применения изменений
Перед любой перезагрузкой конфигурацию проверяют командой nginx -t. Утилита разворачивает все include, подставляет пути, открывает файлы сертификатов и ключей и сверяет их друг с другом. Если ключ не соответствует сертификату, nginx -t сообщит о несовпадении значений и не даст перезагрузить сервер вслепую. Ключ -T пригождается в более широких сценариях: он печатает итоговую конфигурацию целиком, что помогает увидеть, из какого именно файла приехала конкретная директива.
Проверять стоит не только связку, но и содержание новых файлов. Утилита openssl показывает тему, издателя и срок действия:
openssl x509 -in /etc/nginx/ssl/shop.example.com/fullchain.crt -noout -subject -issuer -enddate
Принадлежность ключа сертификату сверяют сравнением открытых частей: из сертификата и из приватного файла извлекают открытый ключ и считают контрольную сумму.
openssl x509 -in fullchain.crt -pubkey -noout | openssl sha256
openssl pkey -in server.key -pubout | openssl sha256
Совпавшие суммы подтверждают, что пара подобрана верно. Такая проверка занимает меньше минуты и снимает самую обидную причину неудачной перезагрузки: расхождение ключа и сертификата, замеченное уже после команды. Заодно убеждаются, что в файле цепочки лежат все промежуточные сертификаты, иначе часть клиентов увидит недоверенный путь, хотя некоторые клиенты иногда прощают неполную цепочку за счёт собственного кэша.
Команда nginx -s reload и порядок действий при обновлении сертификата
Когда файлы уложены и проверки пройдены, остаётся отправить команду. Порядок работы на одном сервере выглядит так:
- новые файлы сертификата и ключа укладываются на постоянные места, предыдущая версия сохраняется рядом для возможного отката;
- пара ключа и сертификата сверяется утилитой openssl, сроки действия перепроверяются;
- конфигурация проходит проверку nginx -t, ошибки ищутся до перезагрузки, а не после;
- серверу отправляется плавная перезагрузка командой nginx -s reload или systemctl reload nginx;
- журнал ошибок просматривается на предмет сообщений о неудачной сборке конфигурации;
- свежая цепочка проверяется запросом через curl со сравнением отпечатка с ожидаемым значением.
Команда nginx -s reload читает идентификатор главного процесса из pid-файла, по умолчанию /run/nginx.pid, и отправляет ему сигнал перечитать конфигурацию. Вызов systemctl reload nginx в сборках большинства дистрибутивов делает то же самое: юнит содержит строку ExecReload с той же командой, поэтому разница между вариантами чисто организационная.
После команды стоит заглянуть в журнал /var/log/nginx/error.log. Удачная перезагрузка сопровождается сообщением о новом наборе рабочих процессов, а неудачная открыто называет строку конфигурации, где разбор сломался. Сервер при этом продолжает отвечать со старыми настройками, так что времени на исправление достаточно. Отдельный практический штрих: перезагрузку можно отправлять повторно, не дожидаясь завершения предыдущей, механизм аккуратно разводит несколько поколений рабочих процессов, хотя доводить до такого без нужды не стоит.
Контроль результата через curl с верификацией цепочки и сравнением отпечатка
После перезагрузки нужен взгляд со стороны клиента. Утилита curl с ключом -v печатает весь ход рукопожатия TLS: издателя, срок действия, совпадение имени в сертификате с запрошенным хостом и итог верификации цепочки. Чтобы не засорять экран телом ответа, тело отбрасывают, а подробный вывод фильтруют:
PROTO="https"
curl -vsSI "$PROTO://shop.example.com/" -o /dev/null 2>&1 | grep -iE "subject|issuer|expire|SSL connection|verify"
Строка с пометкой SSL certificate verify ok означает, что curl собрал цепочку до доверенного корня и не нашёл расхождений. Если сертификат подписан внутренним корпоративным издателем, путь к его корневому файлу передают ключом --cacert, иначе верификация закончится отказом не из-за проблем сервера, а из-за незнакомого корня.
Самый строгий контроль даёт сравнение отпечатка. Ожидаемое значение снимают с нового файла, фактическое снимают с живого сервера, после чего строки сравнивают между собой:
openssl x509 -in /etc/nginx/ssl/shop.example.com/fullchain.crt -noout -fingerprint -sha256
echo | openssl s_client -connect shop.example.com:443 -servername shop.example.com 2>/dev/null | openssl x509 -noout -fingerprint -sha256
Совпавшие отпечатки закрывают вопрос: клиент видит именно тот сертификат, что был уложен на диск. Дополнительный приём для проверки конкретного узла за балансировщиком даёт ключ --resolve: он направляет запрос на выбранный адрес, минуя общую запись DNS, и позволяет убедиться в каждом сервере по отдельности ещё до переключения трафика.
Символические ссылки, долгоживущие соединения и несколько серверов за балансировщиком
Схемы с автоматическим продлением хранят версии в архивных каталогах, а рабочие пути делают символическими ссылками. Продление заменяет цель ссылки, конфигурация при этом не правится. Поведение nginx здесь ровно то, на что надеются: при плавной перезагрузке главный процесс раскрывает каждую ссылку заново и читает файл, на который она смотрит в момент команды. Новые рабочие процессы получают свежую версию, а процессы предыдущего поколения продолжают держать старую в памяти до закрытия своих подключений. Тонкость одна: ссылка должна обновляться атомарно, заменой через временное имя и переименование, иначе между удалением и созданием ссылки может случиться перезагрузка, и проверка увидит отсутствующий путь.
Второй нюанс связан с долгоживущими соединениями. Обычные страницы закрываются за доли секунды, и старые рабочие процессы уходят почти сразу. Соединения WebSocket, серверных событий и длинных опросов висят минутами и часами, задерживая завершение старого поколения. Директива worker_shutdown_timeout ставит этому предел: по истечении заданного времени соединения принудительно завершаются, и процессы освобождают память. Для большинства сайтов достаточно значения в десятки секунд.
На ферме из нескольких серверов за балансировщиком сертификаты меняют по одному узлу за раз. Один сервер выводят из ротации, обновляют, проверяют отпечаток запросом с адресной проверкой узла, возвращают под нагрузку и только потом берут следующий. Такой темп гарантирует, что часть фермы в любой момент отдаёт рабочую конфигурацию, а ошибка в файлах не успевает разъехаться по всем машинам.
Откат на предыдущий сертификат и повторная проверка той же командой
Даже выверенная процедура иногда требует возврата: новый сертификат оказался с неполной цепочкой, срок истёк раньше ожидаемого или внутренние клиенты не доверились издателю. Откат опирается на копию, сохранённую при подготовке. Предыдущий каталог возвращают на рабочее место, конфигурацию проверяют nginx -t и отправляют ту же команду плавной перезагрузки. Сервер заново читает файлы, новая партия рабочих процессов начинает отдавать прежнюю цепочку, и через несколько секунд запрос с проверкой отпечатка подтверждает возврат. Ничего дополнительно останавливать не требуется: механизм, который менял сертификат без простоя, точно так же возвращает его.
Полезно помнить про сопутствующие детали. При включённом ssl_stapling сервер после перезагрузки заново запрашивает статус сертификата у издателя, и первые секунды ответы могут уходить без вложенного статуса. Мониторинг, следящий за сроком действия и отпечатком, заранее обучают новым значениям, иначе после успешного обновления придут тревожные уведомления о подмене. И последнее: старые файлы не удаляют сразу, а держат один цикл продления, потому что копия сертификата за пару килобайт способна сэкономить вечер, если новое продление сорвётся.
Вся процедура помещается в короткий отрезок: подготовленные файлы, проверенная конфигурация, одна команда, контрольный запрос. Привычка проходить эти шаги в одном порядке превращает обновление сертификатов из события, требующего ночного окна, в спокойную рутину, о которой напоминает разве что календарь продления.