Le WordPress d'aujourd'hui, décodé pour les développeurs

Outils & workflow

« SSL: CERTIFICATE_VERIFY_FAILED » sur un déploiement automatisé DDEV

En pleine intégration continue, DDEV refuse une requête sortante avec une erreur de certificat. Diagnostic d'un problème classique côté conteneur, et correctif durable.

Par Clément Hadrot • 23 juin 2025 • 5 min de lecture • Aucun commentaire
« SSL: CERTIFICATE_VERIFY_FAILED » sur un déploiement automatisé DDEV

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.

L'essentiel à retenir : L'erreur vient du magasin de certificats interne du conteneur ; mkcert génère une autorité que le conteneur ne connaît pas toujours ; La correction se joue au niveau de l'image, pas du site

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 :

  1. 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.
  2. Le cache de build Docker restaure une couche ancienne de l’image, antérieure à la régénération de l’autorité locale.
  3. La variable d’environnement pointant vers le magasin de certificats de confiance (SSL_CERT_FILE ou é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ômeCause probableCorrectif
Erreur uniquement en CIAutorité mkcert absente du conteneurddev exec mkcert -install
Erreur après changement de cache DockerCouche d’image obsolète restauréeReconstruire l’image sans cache
Variable curl.cainfo videConfiguration PHP incomplèteDé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.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi