Insérer une image dans un mail PHP : le guide
Beaucoup de développeurs PHP commettent la même erreur au départ : ils référencent une image avec une URL locale du type http://127.0.0.1/monimage.png dans le corps d'un email. Le résultat est prévisible : le client mail affiche une case vide. Le serveur de messagerie du destinataire ne peut pas accéder à votre machine locale. Pour insérer une image dans un mail PHP correctement, il faut encoder le fichier et structurer le message selon les normes MIME. Ce guide couvre chaque étape, de la validation du fichier à l'envoi final.
Comprendre l'encodage base 64 pour les images dans un mail PHP
Le protocole email ne transporte que du texte. Une image est un fichier binaire. Pour franchir cette contrainte, l'encodage base 64 convertit les données binaires en une chaîne de caractères ASCII transmissible sans corruption. Chaque image que vous souhaitez intégrer dans un mail PHP doit passer par cette transformation, sans exception.
Lire et convertir le fichier image
La première étape consiste à ouvrir le fichier avec fopen() en mode lecture, puis à récupérer son contenu binaire via fread(). On applique ensuite chunksplit(base64encode()) sur ce contenu pour produire une chaîne encodée, découpée en lignes de 76 caractères, conformément aux spécifications RFC. La fonction basename() permet de récupérer proprement le nom du fichier sans son chemin complet.
Voici les fonctions essentielles à maîtriser pour cette étape :
fopen($chemin, 'rb'): ouvre le fichier en lecture binairefread($handle, filesize($chemin)): lit tout le contenuchunksplit(base64encode($contenu)): encode et formatebasename($chemin): extrait le nom du fichier
La structure MIME et le boundary
Un email contenant texte et image doit être déclaré avec Content-Type multipart/mixed dans ses en-têtes généraux, accompagné de MIME-Version : 1.0. C'est la norme MIME qui permet de transporter plusieurs types de contenu dans un seul message.
Le boundary est le délimiteur qui sépare chaque section du message. On le génère dynamiquement avec md5(uniqid(rand())) pour garantir son unicité à chaque envoi. Il apparaît dans l'en-tête Content-Type sous la forme boundary="votre_délimiteur", puis précède chaque bloc de contenu dans le corps du message. Sans lui, le client mail est incapable de distinguer le texte HTML de l'image encodée.
Pour éviter que le boundary ne se retrouve accidentellement dans le contenu du message (ce qui casserait le parsing), md5() assure une chaîne hexadécimale de 32 caractères, pratiquement impossible à reproduire dans un contenu réel.

Construire et envoyer un mail PHP contenant une image
Une fois l'image encodée et le boundary défini, il faut assembler le corps complet du message. La section texte HTML s'insère en premier, précédée du boundary et de l'en-tête Content-Type : text/html; charset=utf-8. L'image suit, dans un second bloc délimité par le même boundary.
Les en-têtes spécifiques à l'image
Chaque bloc image nécessite deux en-têtes spécifiques. D'abord, Content-Type : image/png ou image/gif selon le format. Ensuite, Content-Transfer-Encoding : base64 pour indiquer le mode d'encodage utilisé.
Le comportement de Content-Disposition mérite qu'on s'y attarde. Avec la valeur attachment, l'image s'affiche en pièce jointe téléchargeable. Avec la valeur inline, elle s'intègre directement dans le corps du message. En pratique, les tests montrent que les deux démarches peuvent afficher l'image selon le client mail utilisé. Pour une intégration visuelle propre, préférez inline.
Assembler le body et appeler mail()
Le body complet se construit par concaténation : boundary d'ouverture, section texte, boundary de séparation, en-têtes image, contenu encodé en base 64, boundary de fermeture. La fonction mail() reçoit ensuite le destinataire, le sujet, ce body combiné et les en-têtes généraux. Simple en apparence, mais chaque saut de ligne manquant dans les en-têtes peut faire échouer silencieusement l'envoi.
Notez qu'une messagerie sécurisée peut filtrer ou bloquer les emails contenant des images encodées en base 64 si les en-têtes MIME sont mal formés. Soignez la structure.

Valider et gérer les fichiers image avant l'envoi en PHP
Envoyer une image sans la valider au préalable, c'est prendre un risque. Un fichier malveillant déguisé en PNG peut causer des dégâts sérieux. La validation n'est pas optionnelle.
Vérifier l'extension, la taille et l'intégrité
Commencez par extraire l'extension du fichier avec strtolower(strrchr($nom, '.')) ou explode('.', $nom). Vérifiez que cette extension figure dans une liste blanche (png, jpg, gif). La taille maximale se contrôle via $FILES['file']['size'] : une limite à 400000 bytes est un exemple réaliste pour des images légères destinées à un mail. Enfin, $FILES['file']['error'] == 0 confirme que l'upload s'est déroulé sans incident côté serveur.
La fonction getimagesize() retourne un tableau dont l'index 2 indique le format : 1 pour GIF, 2 pour JPEG, 3 pour PNG. C'est une vérification réelle du contenu binaire, bien plus fiable que l'extension seule.
Gérer le fichier et préparer l'envoi
Utilisez uniqid('', true) pour générer un nom de fichier unique et éviter tout écrasement. La fonction copy($FILES['photo']['tmpname'], $destination) déplace le fichier temporaire vers son emplacement final. Si le dossier de destination n'existe pas encore, mkdir($dossier, 0777, true) le crée avec les droits appropriés.
| Opération | Fonction PHP | Remarque |
|---|---|---|
| Lire le format réel | getimagesize() | Index 2 : 1=GIF, 2=JPEG, 3=PNG |
| Redimensionner | imagecreatetruecolor() + imagecopyresampled() | Bibliothèque GD requise |
| Exporter en JPEG | imagejpeg() | Qualité de 0 à 100 |
| Gérer la transparence PNG | imagecolortransparent() | Avant imagecreatefrompng() |
La bibliothèque GD permet de redimensionner une image avant l'envoi, via imagecreatefromjpeg() ou imagecreatefrompng(), puis imagecopyresampled() pour produire une miniature propre. Sous MAMP, GD est activée par défaut. Certains hébergeurs mutualisés la désactivent pour préserver leurs ressources : vérifiez avec phpinfo(). La qualité JPEG se règle entre 0 et 100 dans imagejpeg().
Si vous stockez les noms de fichiers en base de données, utilisez PDO avec des requêtes préparées ($db->prepare() et execute([])) pour neutraliser tout risque d'injections SQL. Récupérez ensuite les enregistrements avec fetch() dans une boucle. C'est la seule approche sérieuse dès que des données utilisateur entrent dans une requête.
Partager cet article