Servir des images de projet avec jsDelivr et GitHub Pages
Pour un petit projet public, vous pouvez servir une image depuis GitHub via une URL jsDelivr épinglée à un commit. Ce guide prépare un PNG, publie le fichier généré et ajoute une copie facultative sur GitHub Pages avec un repli JavaScript. Les deux URL pointeront vers la même image.
Choisir des ressources adaptées aux services
jsDelivr est un service CDN, pas une bibliothèque JavaScript à installer. Son point de terminaison GitHub récupère directement les fichiers du dépôt ; GitHub Pages n’est pas un prérequis. Pages et jsDelivr sont deux façons alternatives de distribuer les fichiers. Aucun des deux ne redimensionne ni ne recompresse le PNG de cet exemple.
Utilisez cette configuration de CDN d’images gratuit pour des ressources appartenant à un projet public, comme une capture d’écran dans une démo cartographique open source. La politique d’utilisation de jsDelivr interdit l’hébergement de fichiers ou de médias à usage général, y compris le stockage des fichiers téléversés sur un site d’hébergement d’images. Elle reconnaît explicitement les projets légitimes, tels que les applications et les jeux comportant des ressources images. Dotez votre projet d’une documentation publique et d’une licence appropriée, et ne publiez que des images que vous pouvez distribuer.
GitHub Pages est disponible pour les dépôts publics avec GitHub Free. Ses limites comprennent une limite de 1 GB pour le site publié et une limite souple de bande passante de 100 GB par mois. Pages restreint également l’utilisation du service pour exploiter une activité commerciale en ligne, un site de commerce électronique ou un SaaS commercial. Ces services conviennent mal aux fichiers privés téléversés ou à une activité d’hébergement d’images à usage général.
Préparer un PNG pour la publication
Commencez dans une copie locale de votre projet public, sur sa branche main. Le dépôt d’exemple
s’appelle map-demo ; remplacez YOUR-USERNAME et map-demo dans les URL par votre compte et votre
dépôt. Ce guide suppose un projet Yarn 4, Node.js 24 ou une version ultérieure, un shell POSIX et
aucun site existant dans docs/. Le flux de travail local de traitement d’images a été testé avec
Node.js 26.8.1 et sharp 0.35.4 sous Linux.
Installez le processeur d’images sharp et créez les répertoires d’entrée et de publication :
corepack yarn add --dev --exact sharp@0.35.4 &&
mkdir -p original-images docs/images
Placez une capture d’écran PNG statique, en sRGB 8 bits, dans original-images/map.png. Le HTML ci-dessous suppose
qu’elle mesure 640 × 360 pixels ; adaptez les dimensions HTML et le texte alternatif à votre image.
Enregistrez ceci sous optimize.cjs à la racine du dépôt. Ce script traite les fichiers .png situés
directement dans original-images/ et écrit des fichiers de même nom dans un nouveau répertoire de version :
const fs = require('node:fs/promises')
const path = require('node:path')
const sharp = require('sharp')
async function optimizeImage(inputPath, outputPath) {
const image = sharp(inputPath)
const metadata = await image.metadata()
if (metadata.format !== 'png') throw new Error(`Expected a PNG: ${inputPath}`)
await image.png({ compressionLevel: 9, palette: false }).toFile(outputPath)
}
async function processDirectory(inputDir, outputDir) {
const files = await fs.readdir(inputDir)
// A published version must not be overwritten by a later run.
await fs.mkdir(outputDir)
for (const file of files) {
const inputPath = path.join(inputDir, file)
const stat = await fs.stat(inputPath)
if (!stat.isFile() || !file.endsWith('.png')) continue
await optimizeImage(inputPath, path.join(outputDir, file))
}
}
processDirectory('original-images', 'docs/images/v1').catch((error) => {
console.error(error)
process.exitCode = 1
})
Exécutez-le une fois :
corepack yarn node optimize.cjs
Ouvrez docs/images/v1/map.png et comparez-le à l’original. Le script conserve ses dimensions et utilise la
compression PNG sans quantification de palette. sharp convertit normalement en sRGB et supprime les
métadonnées. Un fichier plus petit n’est pas garanti : comparez les tailles avant d’adopter le
résultat. Définir l’option PNG quality activerait la quantification de palette et pourrait faire
perdre des couleurs ; consultez les options de sortie de sharp.
Une nouvelle exécution échoue si docs/images/v1 existe déjà, ce qui laisse cette version intacte. Une
entrée corrompue provoque aussi une sortie en erreur ; elle peut laisser un nouveau répertoire
partiellement écrit. Ne publiez pas ce répertoire. Après avoir corrigé l’entrée, supprimez
uniquement le répertoire de sortie en échec et non publié avant de réessayer.
Commiter l’image générée
Vérifiez que docs/images/v1/map.png est bien le PNG réel, et non un pointeur Git LFS. Commitez la ressource
générée elle-même afin que jsDelivr puisse la récupérer. Depuis la racine du dépôt, sans aucune
modification non liée indexée :
git add docs/images/v1/map.png &&
git commit -m "Add versioned map screenshot" &&
git push origin main &&
git rev-parse HEAD
Conservez le hash complet du commit affiché par la dernière commande. Ci-dessous, COMMIT-SHA désigne
ce hash, issu du commit contenant le PNG. Conservez aussi optimize.cjs, package.json et yarn.lock avec
les sources de votre projet ; node_modules/ n’a pas sa place dans le commit.
Utiliser l’URL jsDelivr épinglée à un commit
L’URL de votre image est :
https://cdn.jsdelivr.net/gh/YOUR-USERNAME/map-demo@COMMIT-SHA/docs/images/v1/map.png
Ouvrez-la dans un navigateur après avoir remplacé les espaces réservés. Elle devrait afficher le PNG généré. Aucun compte jsDelivr ni déploiement Pages n’est nécessaire. Il s’agit du format d’URL GitHub documenté.
Utilisez un hash de commit complet plutôt que main, latest ou une version omise. jsDelivr
met en cache de façon permanente les versions statiques et les URL de commit
et leur attribue des en-têtes de cache de longue durée. Publiez les images modifiées dans un nouveau
commit et mettez à jour l’URL ; supprimer l’original de GitHub ne retire pas de manière fiable une
copie en cache.
Ajouter GitHub Pages comme repli facultatif
Vous pouvez utiliser l’URL jsDelivr dans votre propre site immédiatement. Pour offrir à cet exemple
un second chemin de distribution, publiez docs/ via Pages. Enregistrez cette page sous docs/index.html :
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Map demo</title>
<script src="image.js" defer></script>
</head>
<body>
<h1>Map demo</h1>
<img id="my-image" alt="Screenshot of the map demo" width="640" height="360" />
<noscript>Enable JavaScript to load this image demo.</noscript>
</body>
</html>
Fiabilité et repli
Enregistrez ce qui suit sous docs/image.js. Remplacez YOUR-USERNAME et COMMIT-SHA, et remplacez
map-demo si votre dépôt porte un autre nom. Utilisez le hash du commit de l’image obtenu à l’étape
précédente ; la page et le script peuvent être commités plus tard.
function loadImage(imageElement, primarySrc, fallbackSrc) {
imageElement.onerror = function () {
imageElement.onerror = null
console.warn('Primary CDN failed, using fallback')
imageElement.src = fallbackSrc
}
imageElement.src = primarySrc
}
const img = document.getElementById('my-image')
if (!(img instanceof HTMLImageElement)) throw new Error('Missing image element: my-image')
loadImage(
img,
'https://cdn.jsdelivr.net/gh/YOUR-USERNAME/map-demo@COMMIT-SHA/docs/images/v1/map.png',
'https://YOUR-USERNAME.github.io/map-demo/images/v1/map.png',
)
Le gestionnaire bascule une seule fois vers Pages en cas d’erreur d’image. Si les deux origines échouent, il laisse le texte alternatif de l’image disponible et cesse de réessayer. Il n’a aucun délai d’expiration pour une requête qui reste en attente, et les deux chemins partagent toujours GitHub comme source. Il s’agit d’un repli limité, pas d’une garantie de disponibilité.
Créez un fichier docs/.nojekyll vide pour
désactiver le traitement Jekyll,
puis publiez la page et le script :
touch docs/.nojekyll &&
git add docs/.nojekyll docs/index.html docs/image.js &&
git commit -m "Add image demo page" &&
git push origin main
Dans l’onglet Settings du dépôt, ouvrez
Pages. Sous Build and deployment,
définissez Source sur Deploy from a branch,
choisissez main et /docs, puis cliquez sur Save. Le
guide de publication
de GitHub décrit ces contrôles et l’exécution de déploiement à vérifier si la publication échoue.
La publication depuis une branche utilise un flux de travail Actions géré par GitHub ; vous n’avez
pas besoin d’un flux de travail d’optimisation personnalisé.
Attendez qu’un déploiement réussisse avant d’ouvrir https://YOUR-USERNAME.github.io/map-demo/.
Cela suppose le domaine par défaut du site de projet, sans domaine personnalisé. Les chemins
diffèrent parce que Pages publie le contenu de docs/, tandis que jsDelivr lit depuis la racine du
dépôt :
| Emplacement | Chemin de l’image |
|---|---|
| Fichier commité | docs/images/v1/map.png |
jsDelivr, après @COMMIT-SHA/ | docs/images/v1/map.png |
Pages, après /map-demo/ | images/v1/map.png |
Pour une mise à jour, remplacez le répertoire de sortie de l’optimiseur par docs/images/v2, générez et
inspectez la nouvelle image, puis commitez-la. Utilisez ce nouveau hash de commit et le chemin v2
dans l’URL CDN du script, et v2 dans son URL Pages. Déployez la nouvelle ressource avant de
basculer les consommateurs. Conservez v1 pour les anciens liens.
Pages n’épingle pas une URL à un commit Git : le répertoire de version ne reste stable que si vous
conservez son contenu inchangé.
Tester votre configuration
Ouvrez d’abord directement les deux URL d’image. Une erreur 404 signifie généralement que le fichier n’a pas été commité à la révision épinglée, que le chemin ou la casse diffère, ou que Pages n’a pas encore déployé le dossier sélectionné. Vérifiez que chaque réponse est un PNG, et non une page d’erreur HTML.
Sur la page de démo, utilisez l’inspecteur réseau de votre navigateur pour confirmer la requête d’image et ses dimensions. Bloquez l’URL exacte de l’image jsDelivr et rechargez : la requête Pages devrait réussir. Bloquez ensuite les deux URL et rechargez : il devrait y avoir une tentative sur chacune, avec le texte alternatif de l’image toujours présent. Débloquez-les une fois terminé. Testez le comportement de chargement de l’image, pas seulement une réponse de page réussie.
Sécurité
En-têtes CORS
Un élément <img> ordinaire provenant d’une autre origine peut s’afficher sans activer CORS.
Lire ses pixels via un canvas impose des exigences CORS supplémentaires.
Cette démo se contente d’afficher l’image. CORS n’est pas une authentification et ne restreint pas
qui peut télécharger une ressource publique.
Empêcher l’intégration directe (hotlinking)
Le JavaScript de votre page ne peut pas empêcher un tiers d’intégrer l’URL publique de l’image. Tenez les images privées à l’écart de ce flux de travail. Pour le contrôle d’accès, utilisez un stockage et un service de distribution capables d’appliquer une autorisation ou des URL signées.
Lorsque vous avez besoin de plus d’une taille d’image
Cet exemple publie un seul PNG à ses dimensions d’origine. Ajouter srcset ou un élément <picture>
ne génère pas d’images plus petites ni de fichiers AVIF/WebP : ces fichiers doivent d’abord être
produits et commités. Un repli responsive doit aussi effacer les candidats srcset et <source> en
échec avant de modifier src ; c’est pourquoi le gestionnaire simple ci-dessus est volontairement
prévu pour un seul <img> sans ces candidats.
Pour des tailles ou des formats dynamiques, envisagez un service de transformation d’images tel que
l’API de traitement d’images de Transloadit.
