SSL: CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate. Ce message apparaît en plein milieu d’un pipeline d’intégration continue, au moment précis où une commande wp-cli exécutée dans un conteneur DDEV tente d’appeler une API externe pour vérifier une licence d’extension. Localement, sur le poste du développeur, la même commande fonctionne sans le moindre souci. C’est cette différence entre l’environnement local et celui de la CI qui rend cette erreur particulièrement frustrante à diagnostiquer.
Cet article se concentre sur le diagnostic et la correction côté conteneur, dans le cadre d’une intégration continue. Il ne traite pas de la configuration HTTPS d’un site en production, qui repose sur des certificats émis par une autorité publique et non par l’autorité locale générée par mkcert.
Symptôme : une requête sortante qui échoue uniquement en CI
DDEV configure automatiquement HTTPS pour les sites locaux grâce à mkcert, un outil qui génère une autorité de certification locale et l’installe dans le magasin de confiance du système hôte. Cette autorité permet au navigateur du développeur de faire confiance aux certificats auto-signés générés pour chaque projet, sans avertissement de sécurité. Le problème survient quand une commande exécutée à l’intérieur du conteneur DDEV, dans le pipeline de CI, tente une requête HTTPS sortante vers un service qui utilise cette même chaîne de confiance, ou plus fréquemment, quand le conteneur de CI ne dispose tout simplement pas de cette autorité locale dans son propre magasin de certificats.

Diagnostic : où chercher exactement
Le message d’erreur mentionne unable to get local issuer certificate, ce qui signifie concrètement que le client HTTP utilisé par PHP, ou par la commande curl sous-jacente, ne trouve pas dans son magasin de certificats l’autorité qui a signé le certificat présenté. Trois causes possibles, dans l’ordre de fréquence observée :
- Le conteneur de CI démarre une image DDEV fraîche, sans l’autorité mkcert installée sur la machine de développement d’origine.
- Le cache de build Docker restaure une couche ancienne de l’image, antérieure à la régénération de l’autorité locale.
- La variable d’environnement pointant vers le magasin de certificats de confiance (
SSL_CERT_FILEou équivalent selon le langage) n’est pas correctement transmise au conteneur exécutant les tests.
Correctif côté conteneur
La commande suivante, exécutée avant les étapes de test dans la définition du pipeline, force la régénération et l’installation de l’autorité de certification locale à l’intérieur du conteneur web utilisé par DDEV :
ddev exec mkcert -install
Si l’erreur persiste malgré cette commande, il faut vérifier que la variable d’environnement lue par le client HTTP en cause pointe bien vers le fichier généré par mkcert. Pour une requête effectuée via PHP et curl, cela se vérifie en inspectant la configuration curl.cainfo du fichier php.ini actif dans le conteneur :
ddev exec php -i | grep cainfo
Un résultat vide indique que PHP retombe sur le magasin système par défaut, qui ne contient pas nécessairement l’autorité mkcert. Il faut alors s’assurer que le fichier de configuration PHP personnalisé du projet définit explicitement ce chemin, ou reconstruire l’image avec l’étape mkcert exécutée avant le démarrage des services applicatifs.
Cas particulier d’un pipeline qui reconstruit l’image à chaque exécution
Certains pipelines reconstruisent systématiquement l’image du conteneur à chaque exécution, sans cache persistant entre les passages. Dans ce cas, il faut intégrer l’étape mkcert -install directement dans le script de démarrage exécuté à chaque lancement du conteneur, plutôt que de la considérer comme une opération ponctuelle réalisée une fois pour toutes :
#!/bin/bash
set -e
mkcert -install
wp core is-installed || wp core install --url="https://exemple.ddev.site" \
--title="Recette" --admin_user=admin --admin_password=admin --admin_email=admin@exemple.fr
wp plugin verify-license --quiet
Prévention pour les prochains projets
Pour éviter de retomber sur cette erreur sur un futur projet, deux réflexes limitent fortement le risque. D’abord, ajouter systématiquement l’étape de régénération de l’autorité locale dans le script d’initialisation du pipeline, plutôt que de compter sur un état hérité de l’image. Ensuite, isoler dans les journaux de CI le message d’erreur complet, avec la trace de la commande qui l’a déclenché, plutôt que de se contenter du dernier message affiché, qui masque souvent l’appel réseau réellement fautif.
| Symptôme | Cause probable | Correctif |
|---|---|---|
| Erreur uniquement en CI | Autorité mkcert absente du conteneur | ddev exec mkcert -install |
| Erreur après changement de cache Docker | Couche d’image obsolète restaurée | Reconstruire l’image sans cache |
| Variable curl.cainfo vide | Configuration PHP incomplète | Définir le chemin du certificat dans php.ini |
Un réflexe qui fait gagner du temps sur ce type d’incident : toujours relancer la commande fautive isolément dans le conteneur, en dehors du pipeline complet, avant de suspecter le réseau ou le service distant.
En résumé
L’erreur SSL: CERTIFICATE_VERIFY_FAILED sur un déploiement automatisé DDEV pointe presque toujours vers la même cause : l’autorité de certification locale générée par mkcert n’a pas été installée dans le conteneur qui exécute réellement le pipeline. La correction tient en une commande, mais elle doit être intégrée durablement dans le script de démarrage du conteneur pour ne pas ressurgir au prochain rafraîchissement de l’image.