HTTPS запросы с cURL в PHP: настройка, варианты, примеры

Раздел: PHP -> HTTP запросы с cURL

Основное эффективное решение: проверка SSL сертификатов при HTTPS запросах

При выполнении HTTPS запросов через PHP cURL критически важно проверять подлинность SSL сертификатов удаленных серверов. Это защищает от атак посредника (MITM) и гарантирует, что соединение установлено с настоящим сервером.

Базовый подход использует константы CURLOPT_SSL_VERIFYPEER и CURLOPT_CAINFO. Первая включает проверку сертификата со стороны cURL, вторая указывает путь к файлу с корневыми сертификатами (CA bundle).


<?php
$ch = curl_init('https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem'); // файл с CA сертификатами
$response = curl_exec($ch);
if (curl_errno($ch)) {
    $error = curl_error($ch);
}
curl_close($ch);
?>
  

Цель этого решения: обеспечить безопасное взаимодействие с внешними API, банковскими системами, платежными шлюзами и любыми ресурсами, где требуется подтверждение подлинности сервера.

Типичная проблема: если не указать CURLOPT_CAINFO, cURL может использовать системный набор сертификатов, который может отсутствовать или быть устаревшим. Это приводит к ошибке SSL certificate problem: unable to get local issuer certificate. Решение: скачать актуальный файл cacert.pem с cURL CA Extract и указать путь к нему.

Другая ошибка: SSL certificate problem: certificate has expired. Проверьте дату сертификата сервера и обновите CA bundle.

Как выполнить HTTPS запрос без проверки сертификата?

В некоторых окружениях (например, локальная разработка с самоподписанными сертификатами) временно можно отключить проверку. Не рекомендуется для продакшн.


curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // отключает проверку имени хоста
  

Цель: быстрый тест или работа в изолированной сети. Случаи использования: внутренние сервисы с самоподписанными сертификатами, временные интеграции на этапе отладки.

Проблема: снижение безопасности. Любой может подделать сертификат. Ошибка: SSL: no alternative certificate subject name matches target host name при включенном проверке хоста. Решение: отключить только проверку peer, но оставить проверку хоста (CURLOPT_SSL_VERIFYHOST, 2), если сертификат выдан на IP.

Как настроить cURL на использование системных корневых сертификатов без отдельного файла?

В некоторых ОС (Linux, macOS) корневые сертификаты хранятся в системном каталоге. cURL может автоматически их использовать.


curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CAPATH, '/etc/ssl/certs'); // путь к каталогу с сертификатами
// или для macOS: '/usr/local/etc/openssl/certs' или '/System/Library/OpenSSL/certs'
  

Цель: упрощение развертывания, когда не хочется таскать файл cacert.pem. Случаи: серверы с предустановленным набором CA, Docker контейнеры с установленными пакетами ca-certificates.

Ошибка: SSL certificate problem: unable to get local issuer certificate если в указанном пути нет нужных файлов или они не обновлены. Решение: убедиться, что пакет ca-certificates установлен.

Как выполнить HTTPS запрос к серверу с самоподписанным сертификатом (добавив его в доверенные)?

Вместо отключения проверки можно передать cURL файл самоподписанного сертификата сервера.


curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/server.crt'); // файл сертификата сервера
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  

Цель: соблюдение безопасности даже при самоподписанных сертификатах, если вы контролируете сервер. Случаи: внутренние API, тестовые стенды.

Ошибка: SSL certificate error: self signed certificate in certificate chain если сертификат не является корневым. Решение: добавить цепочку сертификатов в один PEM файл (серверный + промежуточные + корневой).

Как выполнить HTTPS запрос через HTTPS прокси?

cURL поддерживает туннелирование через прокси с помощью CURLOPT_PROXY и CURLOPT_PROXYPORT. Для HTTPS прокси требуется указать тип.


$proxy = 'https://proxy.example.com:8080'; // или 'http://...' для HTTP прокси
curl_setopt($ch, CURLOPT_PROXY, $proxy);
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS); // если прокси использует HTTPS
// для аутентификации:
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'login:password');
  

Цель: обеспечивает шифрование трафика между клиентом и прокси. Случаи: корпоративные сети, обход блокировок, анонимизация.

Ошибка: SSL connection error при неправильном типе прокси. Решение: проверить, поддерживает ли прокси HTTPS или использовать HTTP прокси.

Как исправить ошибки SSL, связанные с шифрами и протоколами?

Иногда сервер требует определённый набор шифров или версию TLS. Можно настроить CURLOPT_SSL_CIPHER_LIST и CURLOPT_SSLVERSION.


// установка версии TLS
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
// указание конкретных шифров (например, для старых серверов)
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'ECDHE-RSA-AES128-GCM-SHA256');
  

Цель: совместимость с серверами, имеющими ограничения на протоколы. Случаи: интеграция с устаревшими финансовыми системами, настройка под корпоративные политики безопасности.

Ошибка: SSL routines:...:no protocols available если сервер не поддерживает указанную версию. Решение: понизить версию или не задавать CURLOPT_SSLVERSION, чтобы cURL сам выбрал.

Как cURL обрабатывает SNI (Server Name Indication) для HTTPS?

SNI позволяет серверу предъявлять правильный сертификат при множественных доменах на одном IP. cURL автоматически отправляет SNI, если указан домен в URL.


// cURL сам извлекает имя хоста из URL
curl_setopt($ch, CURLOPT_URL, 'https://example.com/api');
// Принудительно задать имя хоста для SNI (если URL содержит IP)
curl_setopt($ch, CURLOPT_RESOLVE, [ 'example.com:443:203.0.113.10' ]);
  

Цель: обеспечить правильное SSL рукопожатие при виртуальном хостинге. Случаи: обращения к серверам, где несколько сайтов на одном IP.

Проблема: если в URL указан IP, cURL не отправляет имя хоста в SNI, сервер может вернуть сертификат по умолчанию, что вызовет ошибку SSL: certificate subject name mismatch. Решение: использовать CURLOPT_RESOLVE или передать заголовок Host вручную.

Как обработать ошибки SSL при помощи verbose вывода?

Для отладки SSL можно включить подробный лог cURL.


curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
  

Затем прочитать лог:


rewind($verbose);
$log = stream_get_contents($verbose);
echo $log;
  

Цель: детальное понимание проблем с сертификатами, цепочками доверия, ошибками шифрования. Случаи: когда стандартный вывод curl_error недостаточен.

Ошибка: большой объем лога, который может забить память. Решение: записывать в файл или ограничивать буфер.

Расширенные примеры работы cURL с HTTPS

1. POST запрос с JSON через HTTPS и проверкой сертификата

Пример

$url = 'https://httpbin.org/post';
$data = json_encode(['key' => 'value']);
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $data,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
HTTP 200
Response body: {
  "json": {"key": "value"},
  ...
}

2. Аутентификация по сертификату клиента (mutual TLS)

Пример

$ch = curl_init('https://secure.example.com/api');
curl_setopt_array($ch, [
    CURLOPT_SSLCERT => '/path/to/client.crt',
    CURLOPT_SSLCERTPASSWD => 'password123',
    CURLOPT_SSLKEY => '/path/to/client.key',
    CURLOPT_SSLKEYPASSWD => 'keypassword',
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/path/to/ca.crt',
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);

Результат: успешное соединение только при наличии валидного клиентского сертификата и ключа.

3. Получение заголовков ответа при HTTPS

Пример

$ch = curl_init('https://api.github.com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER => true, // включить заголовки в вывод
    CURLOPT_NOBODY => false, // получить тело
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
    CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP cURL)'
]);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
curl_close($ch);
echo $headers;
HTTP/2 200
server: GitHub.com
content-type: application/json; charset=utf-8
...

4. Загрузка файла через HTTPS с сохранением на диск

Пример

$url = 'https://example.com/file.zip';
$destination = '/tmp/file.zip';
$fp = fopen($destination, 'w');
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_FILE => $fp,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
    CURLOPT_FOLLOWLOCATION => true, // следовать редиректам
]);
curl_exec($ch);
curl_close($ch);
fclose($fp);
echo 'Файл загружен: ' . filesize($destination) . ' байт';
Файл загружен: 1048576 байт

5. Обработка ошибок SSL с помощью curl_error и curl_errno

Пример

$ch = curl_init('https://self-signed.badssl.com/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Ошибка (' . curl_errno($ch) . '): ' . curl_error($ch);
} else {
    echo 'Успех';
}
curl_close($ch);
Ошибка (60): SSL certificate problem: self signed certificate

6. Использование CURLOPT_RESOLVE для принудительного разрешения IP с SNI

Пример

$ch = curl_init('https://github.com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
    CURLOPT_RESOLVE => [
        'github.com:443:140.82.121.3' // IP сервера
    ],
]);
$response = curl_exec($ch);

Этот пример принудительно разрешает имя github.com в IP 140.82.121.3, сохраняя SNI для корректного SSL рукопожатия.

7. Проверка цепочки сертификатов с помощью CURLOPT_SSL_VERIFYSTATUS (Experimental)

Пример

// Требует OpenSSL >= 1.0.2 и cURL, собранный с поддержкой OCSP
$ch = curl_init('https://example.com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
    CURLOPT_SSL_VERIFYSTATUS => true, // проверка отзыва по OCSP
]);
$response = curl_exec($ch);

При ошибке OCSP: SSL: certificate status verification failed. Используется редко, но для критичных систем может быть полезно.

PHP cURL HTTPS - comments

En
Php curl https (php)