How Guzzle validates SSL certificates when making outbound API requests

Submitted by admin on



Here are the most common Certificate Authorities (CAs) you'll encounter:

Major Commercial CAs

CA

Known For

DigiCert

Enterprise, acquired Symantec/Verisign's SSL business

Sectigo (formerly Comodo)

High volume, affordable certs

GlobalSign

Popular in enterprise and IoT

Entrust

Government and financial sector

GoDaddy

Popular with small/medium sites

IdenTrust

Issues intermediate certs to other CAs

Free / Automated CAs

CA

Known For

Let's Encrypt

Free, automated, widely adopted — backed by Mozilla, EFF, Cisco

ZeroSSL

Free alternative to Let's Encrypt

Buypass

Free tier available, Norway-based

Government / Specialized CAs

CA

Known For

ISRG

The nonprofit behind Let's Encrypt

Actalis

European/Italian government use

SwissSign

Swiss federal government




In Practice

  • Most SaaS APIs (like Constant Contact, Stripe, Twilio) use DigiCert or Sectigo
  • Most modern websites are moving toward Let's Encrypt for automatic renewal
  • IdenTrust is notable because it cross-signed Let's Encrypt's root early on, which is why LE certs were trusted so broadly from the start

All of these are pre-included in Ubuntu's ca-certificates package



Sometime we need to understand how PHP's HTTP client validates SSL certificates when making outbound API requests. 

Here's how it works:

How SSL Trust Works in PHP API Calls

When your Drupal app calls the Constant Contact API, the trust chain works like this:

1. The OS/Server Certificate Bundle PHP (via cURL under the hood) uses a CA certificate bundle — typically the system's trusted CA store or a bundled cacert.pem file. Constant Contact's cert is signed by a trusted CA (like DigiCert), which is already in that bundle, so it's trusted automatically.

2. How Drupal or your phpapp handles it. Guzzle (or a similar HTTP client) delegates the request to PHP's cURL extension. The relevant settings are:

php
CURLOPT_SSL_VERIFYPEER => true  // verifies the cert chain CURLOPT_SSL_VERIFYHOST => 2     // verifies the hostname matches CURLOPT_CAINFO => '/path/to/cacert.pem'  // the CA bundle to trust

Where the CA Bundle Comes From

Source

Location

Linux system store

/etc/ssl/certs/ca-certificates.crt

PHP's bundled certs

Compiled in, or set via curl.cainfo in php.ini

Guzzle's bundle

Pulled from guzzlehttp/guzzle via Composer

You can check what your PHP install is using:

php
var_dump(openssl_get_cert_locations()); // or echo ini_get('curl.cainfo');

Common Issues & Fixes

SSL verification failing in dev/local? Check your CA bundle is up to date:

bash
# On Ubuntu/Debian sudo apt-get update && sudo apt-get install ca-certificates
 # Or point PHP to Mozilla's bundle via Composer composer require guzzlehttp/guzzle  # ships its own cacert.pem

Manually specifying the CA bundle in php.ini:

ini
curl.cainfo = "/etc/ssl/certs/ca-certificates.crt" openssl.cafile = "/etc/ssl/certs/ca-certificates.crt"




 

 

If you need to pass custom Guzzle options (e.g. a specific CA file), look for where the library instantiates its Guzzle client and pass:

php
$client = new \GuzzleHttp\Client([     'verify' => '/path/to/cacert.pem', // or false to disable (dev only!) ]);

The Short Answer

Find out what vendor the third party API service uses.   You can then check that it exists in the system ca store.

# Check if [vendor] certs are present in the system store 
grep -r "[third party vendor]" /etc/ssl/certs/ # Or list all trusted CAs awk -v cmd='openssl x509 -noout -subject' '/BEGIN/{close(cmd)}; {print | cmd}' \ < /etc/ssl/certs/ca-certificates.crt | grep -i [third party vendor]







Your server trusts [Third Party]'s SSL cert because [ThirdParty]'s SSL cert Vendor (their CA) is already in your system/PHP trust store — no special configuration needed in normal production environments. Issues usually only arise when the CA bundle is outdated or missing in containerized/minimal server environments.

Recent Content
Drupal Topics