Images manquantes lors de l'export Markdown vers Word ? Comment nous avons résolu les problèmes de cross-origin et de compatibilité WebP
Lors du développement de la fonctionnalité d’export Markdown vers Word sur md-to.com, nous avons rencontré un bug frustrant : les images dans le contenu Markdown des utilisateurs disparaissaient après l’export vers Word.
La cause profonde impliquait deux couches : la politique de sécurité du navigateur (CORS) et la compatibilité des formats d’image (WebP). Cet article documente le processus de débogage et notre solution finale.
Le symptôme
Un utilisateur colle du Markdown comme ceci dans l’éditeur :


L’aperçu affiche les deux images correctement. Mais dans le fichier Word exporté, les deux images ont disparu — remplacées par un texte de substitution comme [Interface éditeur] et [Icône du site].
Problème 1 : Word ne supporte pas WebP
Cause profonde
Nous utilisons la bibliothèque docx pour générer les documents Word. Son composant ImageRun ne supporte que quatre formats d’image :
type DocxImageType = 'jpg' | 'png' | 'gif' | 'bmp';
Les sites web modernes utilisent massivement le WebP (25-35 % plus petit que le PNG). Lorsque notre code rencontrait un format non supporté, il levait une erreur Unsupported image type et l’image était silencieusement ignorée.
Solution : Conversion automatique via Canvas
Les navigateurs affichent nativement le WebP, nous pouvons donc utiliser l’API Canvas pour le convertir en PNG :
function loadImageViaCanvas(
source: Blob | string,
): Promise<{ blob: Blob; width: number; height: number }> {
return new Promise((resolve, reject) => {
const img = new Image();
const isBlobSource = source instanceof Blob;
if (!isBlobSource) {
img.crossOrigin = 'anonymous';
}
const blobUrl = isBlobSource ? URL.createObjectURL(source) : null;
img.onload = () => {
if (blobUrl) URL.revokeObjectURL(blobUrl);
const { naturalWidth: width, naturalHeight: height } = img;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0);
canvas.toBlob(
(pngBlob) => {
if (pngBlob) resolve({ blob: pngBlob, width, height });
else reject(new Error('Canvas toBlob failed'));
},
'image/png',
);
};
img.onerror = () => reject(new Error('Failed to load image'));
img.src = blobUrl ?? (source as string);
});
}
Cette fonction accepte deux types d’entrée :
- Blob : pour la conversion de format pure des images déjà téléchargées
- Chaîne URL : pour le contournement CORS (plus de détails ci-dessous)
Quel que soit le format d’entrée (WebP, AVIF, etc.), la sortie est toujours en PNG — que Word gère parfaitement.
Problème 2 : CORS bloque le téléchargement des images cross-origin
Après avoir corrigé le problème WebP, nous avons découvert un problème plus fondamental : les images cross-origin ne pouvaient tout simplement pas être téléchargées.
Cause profonde
Notre code utilise fetch() pour obtenir les données de l’image :
const response = await fetch(imageUrl);
const blob = await response.blob();
Lorsque l’URL de l’image est sur un domaine différent du site web (par ex., le site est md-to.com, l’image est sur example.com), la politique CORS (Cross-Origin Resource Sharing) du navigateur bloque la requête :
Access to fetch at 'https://example.com/image.png'
from origin 'https://md-to.com' has been blocked by CORS policy
Pourquoi l’aperçu fonctionne mais pas l’export ?
Parce que le navigateur applique différents niveaux de sécurité selon les opérations :
| Opération | Cross-origin autorisé ? | Pourquoi |
|---|---|---|
Rendu <img src="externe"> | ✅ Oui | Le navigateur affiche les images sans CORS |
Accès aux données via fetch() | ❌ Bloqué | La lecture de données binaires en JS nécessite CORS |
Export <img crossOrigin> + Canvas | ❌ Bloqué | Un canvas « teinté » ne peut pas exporter de données |
fetch({mode: 'no-cors'}) | ❌ Illisible | Retourne un corps vide |
L’aperçu utilise des balises <img> (rendu cross-origin autorisé), tandis que l’export nécessite que JavaScript lise les données binaires de l’image (bloqué par CORS). C’est pourquoi vous pouvez « voir l’image mais pas l’exporter ».
Pourquoi le Base64 n’aide pas non plus
Vous pourriez penser : « Il suffit de convertir les images en Base64 ! »
Mais le Base64 n’est qu’un format d’encodage, pas une méthode d’acquisition de données. Le problème n’est pas « dans quel format stocker » — c’est que « le navigateur ne vous laisse pas accéder aux octets bruts ». Que vous vouliez encoder en Base64, ArrayBuffer ou autre, la première étape est d’obtenir les données — et CORS bloque cela complètement.
Solution : Proxy d’images côté serveur
Puisqu’il n’y a pas de contournement côté navigateur, nous laissons le serveur récupérer les images. Les serveurs n’ont pas de restrictions CORS et peuvent librement requêter n’importe quelle URL.
Nous avons créé une Edge Function légère comme proxy d’images :
Navigateur → /api/image-proxy?url=https://example.com/image.png → Notre serveur → example.com
↑ Pas de restriction CORS
Fonction proxy (déployée en edge) :
export async function onRequest({ request }) {
const { searchParams } = new URL(request.url);
const target = searchParams.get('url');
// Sécurité : uniquement http(s), uniquement types image, limite de taille
const upstream = await fetch(target);
const contentType = upstream.headers.get('content-type');
return new Response(await upstream.arrayBuffer(), {
headers: {
'Content-Type': contentType,
'Cache-Control': 'public, max-age=86400',
'Access-Control-Allow-Origin': '*',
},
});
}
Routage automatique côté client :
function resolveImageSrc(src: string): string {
const resolved = new URL(src, window.location.href).toString();
// Router les images cross-origin via le proxy
if (new URL(resolved).origin !== window.location.origin) {
return `/api/image-proxy?url=${encodeURIComponent(resolved)}`;
}
return resolved;
}
Toutes les requêtes d’images cross-origin deviennent désormais des requêtes same-origin — plus de problèmes CORS.
Architecture finale
URL de l'image dans le Markdown
│
▼
resolveImageSrc()
│
├── Same-origin → Fetch direct
│
└── Cross-origin → /api/image-proxy?url=xxx
│
▼
Edge Function transfert
│
▼
Blob obtenu
│
▼
getDocxImageType() vérifie le format
│
┌─────────┴──────────┐
▼ ▼
jpg/png/gif/bmp webp/avif/autre
Intégrer directement Canvas → PNG → Intégrer
Environnement de développement
Un middleware proxy correspondant a été ajouté au serveur de développement Vite, assurant un comportement cohérent entre le développement et la production.
Mesures de sécurité
Le proxy d’images n’est pas un proxy ouvert sans restrictions :
- N’autorise que les protocoles
http://ethttps:// - Ne transmet que les Content-Types d’images (png/jpeg/gif/bmp/webp/avif/svg)
- Limite de 10 Mo par image
- Cache de 24 heures sur les réponses
Résumé
| Problème | Cause | Solution |
|---|---|---|
| Les images WebP n’apparaissent pas dans Word | La bibliothèque docx ne supporte pas WebP | L’API Canvas convertit en PNG |
| Les images cross-origin ne peuvent pas être téléchargées | Politique de sécurité CORS du navigateur | Proxy Edge Function côté serveur |
| Les images échouent aussi en environnement de dev | localhost → domaine externe est cross-origin | Middleware proxy du serveur dev Vite |
Point clé : Le modèle de sécurité du navigateur distingue « rendu » et « accès aux données ». Ce n’est pas parce qu’une balise <img> peut afficher une image cross-origin que JavaScript peut lire ses données. Lorsque vous avez besoin d’un accès côté client aux ressources cross-origin, un proxy côté serveur est pratiquement la seule approche fiable.
Envie d’essayer ? Rendez-vous sur md-to.com Markdown vers Word, collez du Markdown avec des images externes, et exportez.
Outils associés
Convertir MD en Word (Docx) en ligne - Markdown vers Word gratuit
Convertissez Markdown en documents Word instantanément. Convertisseur en ligne gratuit MD vers DOCX avec aperçu en temps réel. Conservez la mise en forme intacte. Sans inscription !
Convertisseur Word vers Markdown gratuit - Convertir DOCX en MD en ligne
Convertissez des documents Word en Markdown instantanément. Convertisseur en ligne gratuit DOCX vers MD qui préserve titres, tableaux, listes et mise en forme. Sans inscription !
Convertisseur Markdown vers LaTeX gratuit - Convertir MD en TeX en ligne
Convertissez Markdown en LaTeX en ligne gratuitement. Transformez vos documents MD en format TeX pour les articles académiques. Sans inscription !