Если сайт обращается к внешнему сервису и получает ошибку проверки SSL-сертификата, причина может быть в том, что на сервере нет корневого сертификата удостоверяющего центра, выпустившего сертификат этого сервиса.
В статье разберем, как определить, что дело именно в этом, найти нужный сертификат, подключить его в PHP и проверить результат. Отдельно рассмотрим особенность сайтов на WordPress.
В примерах используется DigiCert Assured ID Root CA – действующий корневой сертификат, который на некоторых серверах виртуального хостинга отсутствует. Вместо него подставляйте тот, который нужен вам: порядок действий одинаковый.
Когда нужно устанавливать корневой сертификат
Что такое корневой сертификат
При HTTPS-соединении сайт получает от сервера сертификат и должен подтвердить его подлинность. Сертификат сервера подписан удостоверяющим центром (УЦ), сертификат УЦ – вышестоящим, и так до корневого сертификата, который никем не подписан и служит отправной точкой доверия.
Проверка проходит, только если корневой сертификат центра сертификации заранее есть на сервере – в наборе доверенных. На серверах виртуального хостинга такие сертификаты хранятся отдельными файлами в директории /etc/ssl/certs/:
ls /etc/ssl/certs/ | grep -i DigiCert
DigiCert_Assured_ID_Root_G2.pem
DigiCert_Assured_ID_Root_G3.pem
DigiCert_Global_Root_G2.pem
DigiCert_Global_Root_G3.pem
DigiCert_TLS_ECC_P384_Root_G5.pem
DigiCert_TLS_RSA4096_Root_G5.pem
DigiCert_Trusted_Root_G4.pemСостав набора может отличаться от стандартного для дистрибутива, поэтому нужного корневого сертификата на сервере может не быть. В любом случае в набор не входят корпоративные и ведомственные УЦ с ограниченной сферой применения – если сервис использует сертификат такого УЦ, проверка не пройдет.
На виртуальном хостинге у пользователя нет прав на запись в директорию /etc/ssl/certs/ – добавить сертификат напрямую нельзя. Поэтому недостающий сертификат подключается иначе: в директории сайта создается отдельный файл, а путь к нему указывается в PHP отдельной директивой – как показано далее в статье.
Как понять, что сертификата нет на сервере
Проблема проявляется по-разному, но один из характерных признаков – ошибка в исходящих запросах из PHP. Например:
cURL error 60: SSL certificate problem: unable to get local issuer certificatecURL error 60: SSL certificate problem: self signed certificate in certificate chainНа стороне сайта это выглядит как неработающая интеграция: не проходит оплата, не отправляются данные во внешнюю систему. Сам сайт при этом открывается нормально.
Чтобы проверить причину точно, узнайте, какой УЦ выпустил сертификат сервиса. Подставьте домен сервиса:
openssl s_client -connect example-service.ru:443 -servername example-service.ru -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/^---/p'В выводе s: – кому выдан сертификат, i: – кто выдал. Нужен issuer последнего сертификата в цепочке. Например:
Certificate chain
0 s:jurisdictionC = US, jurisdictionST = Utah, ... CN = assured-id-root-ca.chain-demos.digicert.com
i:C = US, O = "DigiCert, Inc.", CN = DigiCert Assured ID TLS RSA4096 SHA256 2022 CA1
1 s:C = US, O = "DigiCert, Inc.", CN = DigiCert Assured ID TLS RSA4096 SHA256 2022 CA1
i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Assured ID Root CAВ этом примере недостающий корневой сертификат удостоверяющего центра – DigiCert Assured ID Root CA.
Чтобы найти его среди установленных, начните с поиска по организации, которая выпустила сертификат, – это поле O = из issuer, который вы уже определили. Используйте одно слово без пробелов:
ls /etc/ssl/certs/ | grep -i digicertЕсли результатов немного – сразу сравнивайте их с полным искомым названием. Если результатов много (а у крупных УЦ вроде DigiCert, Sectigo или GlobalSign на сервере может быть несколько десятков их сертификатов), сузьте список вторым grep по еще одному слову – уже из полного названия самого сертификата (CN =), а не организации.
Например, для сертификата DigiCert Assured ID Root CA можно использовать команду:
ls /etc/ssl/certs/ | grep -i digicert | grep -i assuredПример вывода:
DigiCert_Assured_ID_Root_G2.pem
DigiCert_Assured_ID_Root_G3.pemДалее вывод нужно разобрать вручную: сравните каждое найденное имя файла целиком с точным названием, которое вы искали, – включая версию. Сертификаты одного УЦ часто выпускаются в нескольких версиях (Root CA, Root G2, Root G3 и так далее), и это разные сертификаты: наличие одной версии не означает, что нужная вам версия тоже установлена. В примере выше есть G2 и G3, но нет версии без номера – именно ее и не хватает.
Если среди найденного нет точного совпадения (или вывод вовсе пустой) – сертификата нет и его нужно установить.
Где скачать файл сертификата
У каждого крупного УЦ есть официальный раздел с корневыми сертификатами. Как правило, корневые сертификаты УЦ размещаются на его официальном сайте, в разделе поддержки или документации. Скачивайте файл только с этого официального сайта.
Учтите, что удостоверяющий центр мог сменить владельца, и его сертификаты публикует уже другая компания. Например, сертификаты Thawte, GeoTrust и RapidSSL сейчас размещены в репозитории DigiCert. Если УЦ непубличный – корпоративный или ведомственный, – запросите файл у администратора сервиса.
Нужен формат PEM – текстовый файл с содержимым вида:
-----BEGIN CERTIFICATE-----
(набор символов в кодировке Base64)
-----END CERTIFICATE-----Расширение может быть .pem или .crt. Если файл скачался в двоичном формате DER (это можно понять по нечитаемым символам, если открыть файл в текстовом редакторе), его нужно перевести в PEM – как это сделать, показано в шаге 2.
Установка корневого сертификата
Файл сертификата размещается в директории сайта, объединяется с набором доверенных корневых сертификатов в один файл, после чего путь к этому файлу указывается в директиве PHP openssl.cafile. Все команды выполняются по SSH из корневой директории сайта – внутри public_html.
Шаг 1. Создайте директорию для сертификата
Выполните команду, находясь в корневой директории сайта:
mkdir certШаг 2. Загрузите файл сертификата
Адрес для скачивания сертификата возьмите с сайта нужного УЦ. Ниже приведен пример:
wget -O cert/root.crt https://cacerts.digicert.com/DigiCertAssuredIDRootCA.crtЕсли скачанный файл окажется в двоичном формате DER, переведите его в PEM – например, при открытии такого файла в текстовом редакторе вместо -----BEGIN CERTIFICATE----- видны нечитаемые символы. Выполните команду на сервере:
openssl x509 -inform DER -in cert/root.crt -out cert/root.pemКоманда не выводит сообщений на экран при успешном выполнении. Файл сертификата после преобразования сохраняется по пути cert/root.pem. Проверьте его следующей командой:
openssl x509 -in cert/root.pem -noout -subject -fingerprint -sha256Команда выведет название УЦ и отпечаток – сверьте их с тем, что опубликовано на сайте УЦ.
В выводе openssl s_client из предыдущего раздела цепочка может обрываться, если сервис, к которому вы обращаетесь, не отдает промежуточный сертификат: в ответе присутствует только сертификат самого сервиса. В этом случае скачайте с сайта УЦ еще и промежуточный сертификат и сохраните его в ту же директорию:
wget -O cert/intermediate.pem ссылка_на_промежуточный_сертификатЧтобы проверить, сколько сертификатов прислал сервис (в примере ниже – example-service.ru), выполните команду:
openssl s_client -connect example-service.ru:443 -servername example-service.ru -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"Если результат 1 – промежуточного сертификата в ответе нет, его нужно скачать отдельно. Если больше – сервис уже присылает его сам, докачивать ничего не нужно.
Шаг 3. Соберите объединенный файл сертификатов
Директива openssl.cafile, которую мы пропишем на шаге 5, указывает PHP файл со списком доверенных корневых сертификатов. Этот файл становится основным источником доверия, поэтому он должен содержать не только ваш сертификат, но и весь остальной набор.
Если указать файл с одним сертификатом, сайт потеряет доверие ко всем остальным сервисам. Поэтому собираем в один файл всё содержимое директории /etc/ssl/certs/ вместе с новым сертификатом. Используйте эту команду, чтобы объединить файлы без повреждения структуры сертификатов:
(for f in /etc/ssl/certs/*.pem; do cat "$f"; echo; done; cat cert/root.pem) > cert/ca-bundle-custom.crtПромежуточный сертификат, если он нужен, добавляется тем же способом:
(for f in /etc/ssl/certs/*.pem; do cat "$f"; echo; done; cat cert/root.pem cert/intermediate.pem) > cert/ca-bundle-custom.crtПроверьте, что сертификатов в получившемся файле стало больше, чем было в директории /etc/ssl/certs/, и что и что между сертификатами сохраняются переносы строк:
ls /etc/ssl/certs/*.pem | wc -l
grep -c 'BEGIN CERTIFICATE' cert/ca-bundle-custom.crt
grep -c 'END CERTIFICATE-----BEGIN CERTIFICATE' cert/ca-bundle-custom.crtПервая команда показывает количество исходных сертификатов, вторая должна быть больше на 1 (или на 2, если добавлялся промежуточный сертификат). Третья команда должна вернуть 0: любое другое значение означает, что в файле есть склеенные сертификаты и объединенный файл нужно пересобрать.
/etc/ssl/certs/ может со временем меняться вместе с обновлениями сервера, а собранная вами копия – нет. Если через несколько месяцев появятся ошибки SSL при обращении к сервисам, которые раньше работали, пересоберите файл той же командой.Шаг 4. Определите полный путь к файлу
В php.ini путь к файлу указывается в абсолютном виде. Чтобы его получить, выполните команду:
realpath cert/ca-bundle-custom.crtПуть строится по схеме:
/home/[первая буква логина]/[логин]/[директория сайта]/public_html/cert/ca-bundle-custom.crtЛогин аккаунта указан в правом верхнем углу панели управления. Директорию, к которой привязан домен, можно посмотреть в разделе «Сайты». Пример результата:
/home/b/betutorial/cert.betutorial.ru/public_html/cert/ca-bundle-custom.crtШаг 5. Подключите файл в PHP
Способ зависит от режима работы PHP – CGI или mod_php (используется по умолчанию). Посмотреть режим можно в разделе «Сайты» панели управления. Нажмите на иконку
в строке с именем сайта:

В открывшемся окне посмотрите, активирован ли Режим cgi:

Если режим CGI включен, добавьте в файл cgi-bin/php.ini директиву openssl.cafile и укажите путь до файла ca-bundle-custom.crt из шага 4:
openssl.cafile = /home/b/betutorial/cert.betutorial.ru/public_html/cert/ca-bundle-custom.crtОткрыть файл на редактирование можно командой vim cgi-bin/php.ini или через файловый менеджер панели управления.
Если режим CGI выключен, директивы из cgi-bin/php.ini не применяются – даже если создать этот файл вручную. Прописать путь можно только через конфигурацию виртуального хоста, которая доступна технической поддержке. Напишите тикет из панели управления и укажите домен сайта и полный путь к объединенному файлу из шага 4.
Особенность сайтов на WordPress
Ядро WordPress не использует ни системный набор, ни файл из openssl.cafile – оно берет сертификаты из собственного файла wp-includes/certificates/ca-bundle.crt. Поэтому сертификат нужно дополнительно добавить в этот файл – независимо от режима PHP:
- Проверьте, что сертификата в файле еще нет.
В этом файле, в отличие от системного набора, у каждого сертификата рядом есть читаемое название. При помощи утилиты grep найдите название по организации, которая выпустила сертификат, и при необходимости уточните запрос вторым словом из названия сертификата:
cat wp-includes/certificates/ca-bundle.crt | grep -i digicert | grep -i assuredСравните результат с точным названием, которое искали, – так же, как в разделе «Как понять, что сертификата нет на сервере». Если точного совпадения нет – сертификата в файле нет, и его нужно добавить.
- Скопируйте сертификат.
Выведите содержимое файла с корневым сертификатом:
cat cert/root.pemСкопируйте блок целиком, вместе со строками -----BEGIN CERTIFICATE----- и -----END CERTIFICATE-----.
- Вставьте сертификат в конец файла
wp-includes/certificates/ca-bundle.crt.
В файле wp-includes/certificates/ca-bundle.crt уже есть остальные корневые сертификаты, поэтому необходимо добавить только новый корневой сертификат. Откройте файл на редактирование командой vim wp-includes/certificates/ca-bundle.crt или через файловый менеджер панели управления и вставьте скопированный сертификат в конец файла.
Если добавляете и корневой, и промежуточный сертификат, вставляйте сначала корневой, затем промежуточный.
ca-bundle.crt входит в состав ядра и перезаписывается при обновлении WordPress – после обновления сертификат придется добавить заново.Проверка
Проверять нужно PHP-скриптом: директива openssl.cafile относится только к PHP, консольная утилита curl ее не читает и использует свой набор сертификатов.
Скрипт показывает, какой файл использует PHP, и проверяет соединение с вашим сервисом и с обычным сайтом. Вторая проверка нужна, чтобы убедиться, что доверие к остальным сертификатам не потерялось. Укажите в $endpoints адрес вашего сервиса, контрольный оставьте как есть. Для примера в статье используется адрес https://assured-id-root-ca.chain-demos.digicert.com:
<?php
$endpoints = [
'Целевой сервис' => 'https://example-service.ru',
'Контрольный сайт' => 'https://www.google.com',
];
$cafile = ini_get('openssl.cafile');
echo '<pre>openssl.cafile: ' . ($cafile !== '' ? htmlspecialchars($cafile) : 'не задан') . "\n";
if ($cafile !== '' && is_readable($cafile)) {
echo 'Сертификатов в файле: '
. substr_count(file_get_contents($cafile), 'BEGIN CERTIFICATE') . "\n";
}
echo "</pre>";
foreach ($endpoints as $name => $url) {
echo '<pre>' . htmlspecialchars($name) . ' – ' . htmlspecialchars($url) . "\n";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CERTINFO => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 20,
]);
$ok = curl_exec($ch) !== false;
$certinfo = curl_getinfo($ch, CURLINFO_CERTINFO);
if ($ok) {
echo "SSL verification: OK\n";
if (!empty($certinfo)) {
$last = end($certinfo);
echo 'Сертификат выдан: ' . htmlspecialchars($last['Issuer'] ?? '–') . "\n";
}
} else {
echo 'ОШИБКА (' . curl_errno($ch) . '): ' . htmlspecialchars(curl_error($ch)) . "\n";
}
curl_close($ch);
echo '</pre>';
}Создайте на сервере файл check-cert.php в корневой директории сайта (например, командой vim check-cert.php), вставьте в него код скрипта и откройте по адресу https://ваш-домен.ru/check-cert.php. Оба адреса должны дать SSL verification: OK.
Как читать результат:
- ошибка у целевого сервиса, контрольный работает – добавлен не тот сертификат или неверно указан путь. Проверьте, что нужный УЦ есть в файле, и сверьте путь в
openssl.cafile; - ошибка у контрольного сайта – в объединенный файл не попал системный набор, пересоберите его по шагу 3;
- openssl.cafile: не задан – директива не применилась. При включенном режиме CGI проверьте
cgi-bin/php.ini, приmod_phpнужно обратиться в техническую поддержку.
Ядро WordPress использует свой файл сертификатов, поэтому его нужно проверить отдельно. Создайте файл check-cert-wp.php в корневой директории WordPress-сайта – там же, где расположен файл wp-load.php:
<?php
require_once __DIR__ . '/wp-load.php';
$endpoints = [
'Целевой сервис' => 'https://example-service.ru',
'Контрольный сайт' => 'https://www.google.com',
];
foreach ($endpoints as $name => $url) {
$response = wp_remote_get($url, ['timeout' => 20, 'sslverify' => true]);
echo '<pre>' . esc_html($name) . ': ';
echo is_wp_error($response)
? 'ОШИБКА – ' . esc_html($response->get_error_message())
: 'SSL verification: OK';
echo '</pre>';
}После проверки удалите оба скрипта: оставлять на сайте служебные файлы с диагностикой небезопасно. Затем проверьте саму интеграцию, для которой устанавливался сертификат, – например, выполните тестовый запрос к сервису или оформите тестовый заказ.
Заключение
Чтобы PHP доверял сервису, чей УЦ отсутствует в системе, нужно определить название этого УЦ, скачать его корневой сертификат с официального сайта, собрать объединенный файл из системного набора и своего сертификата и прописать путь к нему в openssl.cafile.
Главное – не указывать в директиве файл с одним сертификатом: он заменяет собой весь список доверенных корней. На сайтах на CMS WordPress сертификат дополнительно добавляется в ca-bundle.crt ядра CMS.
Если возникнут вопросы, напишите нам, пожалуйста, тикет из панели управления аккаунта (раздел “Помощь и поддержка”), а если вы захотите обсудить, как скачать корневые сертификаты или установить сертификат НУЦ Минцифры, – ждем вас в нашем сообществе в Telegram.