В SSL-настройках чаще всего «ломается» не сам серверный сертификат, а цепочка доверия (TLS chain). Снаружи это выглядит странно: браузер открывает сайт, а мониторинг, curl, Java, Python или почтовый клиент ругаются certificate verify failed. Почти всегда причина — вы отдаёте клиенту неполную или неправильную цепочку: не тот intermediate certificate, перепутанные файлы fullchain.pem/bundle.crt, неверный порядок сертификатов, или вы положились на AIA fetching, который в реальном мире часто не срабатывает.
Что такое TLS chain и почему «промежуточные» важнее, чем кажется
Упрощённо TLS-цепочка состоит из трёх уровней:
Leaf (сертификат сайта) — выдан на домен(ы) и содержит публичный ключ сервера.
Intermediate (промежуточный сертификат) — сертификат центра, который подписал ваш leaf. Промежуточных может быть несколько.
Root (корневой) — самоподписанный, уже присутствует в доверенных хранилищах ОС/браузеров/SDK.
Сервер обычно не должен отдавать root. Его задача — отдать leaf и все необходимые intermediate, чтобы клиент смог построить цепочку до доверенного root из своего trust store.
Правило на практике: сервер обязан отдать клиенту всё, чего не хватает для построения цепочки до доверенного root, но сам root обычно отдавать не нужно.
Термины и имена файлов: fullchain.pem, bundle.crt, intermediate
Названия файлов зависят от CA и инструмента выпуска, но смысл обычно стабильный.
Intermediate certificate (промежуточный)
Это один или несколько сертификатов, которыми CA «прокладывает мост» от вашего leaf до root. Их часто выдают отдельным файлом: intermediate.crt, ca.crt, chain.crt.
bundle.crt
bundle.crt обычно означает «набор цепочки»: в одном файле подряд лежат промежуточные сертификаты. Иногда поставщик добавляет туда и root (это бывает в старых пакетах или инструкциях), но для веб-сервера root чаще лишний.
Исторически во многих Apache-сценариях leaf хранили отдельно, а bundle.crt подключали как цепочку. Сейчас чаще используют «fullchain одним файлом», но термин «bundle» в панелях и мануалах встречается постоянно.
fullchain.pem
fullchain.pem почти всегда означает: leaf + intermediate(ы) в правильном порядке. Это самый универсальный формат, особенно для Nginx: один файл в ssl_certificate — и сервер отдаёт клиенту всё необходимое.

Почему возникает certificate verify failed (и почему браузер может «молчать»)
Ошибки в цепочке проявляются по-разному у разных клиентов:
Браузеры часто умеют «дотянуть» промежуточный сертификат сами (AIA fetching) и кэшируют ранее увиденные intermediate. Поэтому «у меня в браузере зелёное» не доказывает корректную цепочку на сервере.
CLI-клиенты и библиотеки (контейнеры с минимальным набором CA, некоторые сборки
curl, Java, старые Android, часть SDK) могут не делать AIA fetching или делать его непредсказуемо.Мониторинги/балансировщики обычно проверяют строго и не полагаются на внешние догрузки.
Типичные причины certificate verify failed:
сервер отдаёт только leaf без intermediate;
сервер отдаёт «не тот» intermediate (например, от другой линейки CA);
неправильный порядок сертификатов в файле (для части клиентов это критично);
в цепочке есть лишний root или посторонний intermediate;
цепочка обрывается на intermediate, который не ведёт к root из trust store клиента;
используется cross-signed цепочка, а выбранный вариант не подходит вашей аудитории.
AIA fetching: почему нельзя на него рассчитывать
AIA (Authority Information Access) — поле в сертификате, где указан адрес, по которому можно скачать промежуточный сертификат. Некоторые клиенты пытаются сходить туда и достроить цепочку (это и называют AIA fetching).
На практике AIA fetching ненадёжен:
клиент может быть в сети без доступа к адресам CA (закрытые контуры, корпоративные прокси);
в окружении клиента могут быть запрещены исходящие запросы;
поведение библиотек отличается по версии и платформе;
при MITM-прокси догрузка может ломаться или фильтроваться.
Вывод простой: корректная TLS chain должна приходить от сервера полностью. AIA — только «страховка» на стороне клиента, а не часть вашей серверной конфигурации.
Как понять, что именно отдаёт сервер: проверяем цепочку руками
1) Смотрим, какие сертификаты реально улетают в TLS
openssl s_client -connect example.com:443 -servername example.com -showcerts
На что смотреть в выводе:
сколько сертификатов показано блоками
-----BEGIN CERTIFICATE-----;первый — обычно leaf (сертификат сайта);
дальше должны идти промежуточные (intermediate), и их должно хватать для построения цепочки до доверенного root;
строка
Verify return codeполезна, но не всегда отражает реальность для «другого» trust store (например, в Java или в минимальном контейнере).
2) Делаем локальную проверку openssl verify
openssl verify удобен, когда вы хотите строго проверить связку «leaf подписан intermediate, intermediate ведёт к root». Важно: команда не использует trust store вашей ОС автоматически, если вы явно не указали -CAfile или -CApath.
Если у вас есть cert.pem (leaf) и chain.pem (intermediate(ы)), проверьте так:
openssl verify -untrusted chain.pem cert.pem
Если нужно проверять с конкретным набором доверенных root (например, корпоративный trust store), используйте:
openssl verify -CAfile roots.pem -untrusted chain.pem cert.pem
3) Быстро проверяем порядок в fullchain
Правильный порядок в fullchain.pem почти всегда такой:
сначала leaf;
затем intermediate, который подписал leaf;
затем следующий intermediate выше по цепочке и так далее.
Корневой сертификат обычно не добавляют.
Практика: как собирать fullchain.pem и bundle.crt без сюрпризов
Обычно CA выдаёт либо готовый fullchain.pem, либо пару файлов «сертификат домена» + «цепочка/bundle». Если у вас раздельно, сборка сводится к конкатенации в правильном порядке.
Сборка fullchain.pem из двух файлов
Допустим, у вас:
cert.pem— leaf;bundle.crt— промежуточные.
Собираем fullchain:
cat cert.pem bundle.crt > fullchain.pem
Дальше проверьте, что файл начинается с leaf и содержит несколько блоков сертификатов.
Как отличить leaf от intermediate
Посмотрите subject и issuer у каждого сертификата:
openssl x509 -in cert.pem -noout -subject -issuer
openssl x509 -in bundle.crt -noout -subject -issuer
У leaf subject — ваш домен/организация, а issuer — промежуточный центр. У intermediate в subject — имя промежуточного центра, а issuer — следующий центр выше (ещё один intermediate или root).
Nginx: что указывать в ssl_certificate и где чаще ошибаются
В Nginx директива ssl_certificate должна указывать на файл, который содержит leaf + intermediate. На практике это почти всегда fullchain.pem.
Типовая конфигурация (упрощённо):
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example/fullchain.pem;
ssl_certificate_key /etc/ssl/example/privkey.pem;
}
Распространённая ошибка: указать в ssl_certificate только cert.pem (leaf). Тогда часть клиентов не сможет достроить цепочку и упадёт на валидации.
Ещё одна ошибка: положить в fullchain.pem лишний root. В большинстве случаев он не нужен и иногда вызывает проблемы у строго проверяющих клиентов.
Если вы выпускаете и обновляете сертификаты автоматически, полезно держать под рукой базовые практики по HTTPS и HSTS: настройка HTTPS, Certbot и HSTS без типовых ошибок.

Apache: SSLCertificateFile, цепочка и нюансы версий
В Apache подход зависит от версии и сборки: где-то встречаются раздельные директивы, где-то принято указывать полный файл цепочки. Практическая цель одна: клиент должен получить intermediate.
Вариант, когда вы используете fullchain одним файлом:
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/example/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example/privkey.pem
</VirtualHost>
Если в вашей схеме «leaf отдельно + bundle отдельно», убедитесь, что Apache действительно подхватывает цепочку, и что bundle содержит только нужные intermediate (без случайного root и без «чужих» веток).
Диагностика «в лоб»: быстрый чек-лист, когда всё равно не работает
Проверьте, что сервер отдаёт больше одного сертификата в
openssl s_client -showcerts.Сверьте
issuer/subject: leaf должен быть выдан тем intermediate, который вы отдаёте следующим.Проверьте порядок: leaf первым, далее цепочка вверх.
Уберите root из отдачи, если добавили его «на всякий случай».
Не полагайтесь на AIA fetching: даже если браузер открывает, CLI может падать.
Сделайте локальный
openssl verifyс-untrusted, чтобы проверить связку leaf+chain.Проверьте, нет ли подмены цепочки на балансировщике/CDN: иногда на edge всё корректно, а на origin — нет (или наоборот).
Подводные камни с кросс-подписью и совместимостью клиентов
Иногда один и тот же intermediate доступен в нескольких вариантах цепочки (например, через разные root, включая cross-sign). Для админа это означает, что «правильная» цепочка может отличаться в зависимости от аудитории: старые Android/Java, встроенные устройства, корпоративные trust store.
Практический подход:
берите цепочку, рекомендованную вашим CA именно для серверов;
проверяйте не только браузером, но и инструментами/клиентами, которые критичны для вашего сервиса (SDK, интеграции, агенты мониторинга);
при проблемах совместимости фиксируйте цепочку явно и документируйте, какой именно intermediate вы используете.
Итог: как перестать путаться в bundle и fullchain
TLS chain — это leaf + промежуточные сертификаты до доверенного root.
Intermediate должен быть отдан сервером, иначе часть клиентов получит
certificate verify failed.fullchain.pem— обычно лучший вариант для веб-сервера: leaf + chain в одном файле.bundle.crt— чаще всего только chain (intermediate), используйте его для сборки fullchain или в схемах, где цепочка подключается отдельно.openssl s_clientиopenssl verify— базовые инструменты, чтобы увидеть, что реально отдаёт сервер, и проверить связку локально.
Если привести конфигурацию к простому правилу «Nginx получает fullchain.pem, Apache — эквивалентную полную цепочку», то большая часть загадочных ошибок валидации исчезает. А если для проекта критичны стабильность и предсказуемость обновлений, удобнее держать сертификаты и веб-сервисы на управляемой площадке — от виртуального хостинга до VDS под вашу схему деплоя.


