Si vous avez cherché “Puppeteer PDF alternative” aujourd’hui, la vraie question ressemble probablement à ceci :
“Pourquoi ma fonction serverless met-elle 2 secondes à démarrer à froid et utilise-t-elle 900 MB de RAM juste pour imprimer une facture ?”
Puppeteer est un excellent outil. Il est aussi largement surdimensionné pour le travail que beaucoup d’équipes lui confient : transformer des données structurées en PDF prévisible. Cet article s’adresse à l’équipe qui s’apprête à envoyer Puppeteer en production et se demande discrètement s’il existe une option plus saine.
Nous allons couvrir les cas où Puppeteer mérite son poids, ceux où ce n’est pas le cas, et la matrice de décision réelle en 2026.
Ce que vous expédiez réellement avec Puppeteer
Quand vous lancez npm install puppeteer, vous téléchargez environ 170 MB de Chromium avant même les dépendances transitives. À l’exécution, Chromium headless demande souvent 600 à 900 MB de mémoire résidente pour rendre une seule page, et 1 à 2 secondes de démarrage à froid pour lancer le navigateur. Chaque rendu doit :
- Démarrer le processus navigateur, ou réutiliser un pool.
- Ouvrir un nouvel onglet.
- Naviguer vers votre HTML ou votre URL.
- Attendre
domcontentloaded, et souvent les polices, images et composants web. - Exécuter
page.pdf(), qui sérialise la page peinte via le moteur PDF de Chromium. - Fermer l’onglet.
C’est la taxe de la plateforme web complète. Vous la payez que votre document soit un contrat juridique de 90 pages avec graphiques SVG intégrés ou une étiquette d’expédition d’une page avec cinq lignes de texte.
Pour les cas HTML-to-PDF où l’entrée a réellement besoin de mise en page CSS, de contenu piloté par JavaScript, de polices web et du reste de la plateforme web, cette taxe est justifiée. Pour le reste, factures, étiquettes, reçus, billets, relevés et certificats, elle revient souvent à brûler du budget.
Où Puppeteer gagne
Soyez honnête sur ces points, sinon votre équipe remettra la décision en question plus tard :
- Rendu HTML/CSS fidèle. Si votre design system émet du HTML et que vous voulez un PDF identique à ce HTML au pixel près, Puppeteer est imbattable. C’est littéralement Chrome qui imprime.
- Fonctionnalités de la plateforme web. SVG avec filtres, cas limites de CSS Grid, composants web, contenu évalué par JavaScript, iframes tierces : tout fonctionne.
- Débogage visuel. Vous pouvez prendre une capture pendant le rendu, ouvrir DevTools en mode headless et voir exactement ce que le moteur voit.
- Aucune étape de traduction. Si votre contenu est déjà une page web, il n’y a pas d’adaptation de schéma.
page.goto(url); await page.pdf()est tout le pipeline.
Si deux de ces points décrivent votre vraie charge, ne migrez pas. Puppeteer est le bon choix.
Où Puppeteer perd, nettement
Dans les autres cas, l’empilement des coûts devient rapide.
Mémoire et démarrage à froid en serverless
Une Lambda Node 20 ou un Cloudflare Container typique avec Puppeteer :
| Métrique | Valeur typique |
|---|---|
| Taille de l’image conteneur | 250 à 400 MB, Chromium + Node + votre code |
| Temps de démarrage à froid | 1,8 à 2,5 secondes |
| RAM chaude par rendu | 600 à 900 MB |
| Rendus concurrents par instance 1 GB | 1, parfois 2 si les pages sont minuscules |
Si votre service de factures traite 100 000 rendus/mois, vous payez l’énergie de démarrage du navigateur à chaque conteneur froid, alors qu’aucun de ces rendus n’avait besoin d’exécuter JavaScript.
Le piège des polices dans les conteneurs
Chromium embarque un jeu de polices par défaut, souvent incomplet pour CJK, cyrillique, devanagari, arabe et de nombreux glyphes propres à certains systèmes d’écriture. En production, la découverte ressemble à ceci :
La facture T3 2025 du bureau de Tokyo imprime
▢▢▢▢ 2025年第3四半期. Le client escalade. Votre équipe passe un sprint à déboguer les installations de polices dans le Dockerfile et les règles CSS de fallback.
Ajouter NotoSans CJK ajoute à lui seul ~50 MB à votre image. Ajouter un jeu Noto de fallback global ajoute ~250 MB. Vous payez Chromium et une cathédrale de polices pour imprimer une facture japonaise.
Déterminisme
Les rendus Puppeteer ne sont pas identiques octet pour octet entre versions de Chromium. Une mise à jour de patch peut déplacer subtilement le crénage, les lignes de base de polices ou les positions de saut de page. Si votre suite de tests compare les PDF, et elle devrait le faire, chaque mise à jour de Chromium devient une petite enquête : quel rendu a changé, et était-ce intentionnel ?
JavaScript au moment du rendu
Même une page HTML “statique” doit être parsée, mise en page, peinte puis sérialisée. En pratique, cela représente 80 à 400 ms par page sur un processus chaud. La majeure partie du temps est dans la mise en page, pas dans la peinture.
À titre de comparaison, un service qui envoie du JSON directement à un moteur binaire rend la même facture d’une page en 3 à 8 ms.
Où gPdf s’insère
gPdf inverse le modèle : au lieu de décrire votre document en HTML et de demander à un navigateur de le peindre, vous le décrivez en JSON structuré, sous forme de DocumentRequest, et un moteur Rust compilé en WebAssembly émet directement le PDF. Pas de navigateur. Pas de DOM. Pas de passe de mise en page JavaScript.
Cela paraît restrictif, et ça l’est pour les problèmes vraiment façonnés par le HTML. Mais pour la classe de documents facture / étiquette / reçu / relevé / certificat, le modèle JSON-first est souvent plus adapté :
- Vos données sont déjà structurées. Votre facture existe déjà quelque part comme objet
{ customer, lines, totals, taxes, notes }. Vous ne voulez pas d’abord la rendre en HTML, puis demander à un navigateur de relire ce HTML pour reconstruire une mise en page. Vous voulez aller directement de la donnée au PDF. - La mise en page devient un contrat. Quand
font_size: 11signifie toujours 11 points etgap: 8signifie toujours 8 points, deux ingénieurs qui relisent une PR voient la même sortie. Il n’y a pas d’écart d’interprétation autour dedisplay: flex. - La sortie est identique octet pour octet. Même entrée -> mêmes octets. Vous pouvez comparer deux PDF et ne voir que ce qui a réellement changé.
- Le démarrage à froid est celui du runtime, pas celui d’un navigateur. Un isolate V8 sur Cloudflare Workers démarre en 5 à 20 ms. Le module WASM reste chaud en mémoire entre les invocations sur le même isolate.
Un rendu gPdf typique d’une facture d’une page se situe autour de 3 à 5 ms p50 de bout en bout à l’edge, depuis la colo Cloudflare touchée par l’utilisateur. C’est environ deux ordres de grandeur plus rapide que le chemin chaud de Puppeteer, et trois ordres de grandeur plus rapide que son chemin froid.
La matrice de décision
Utilisez la table que vous utiliseriez vraiment en revue d’architecture.
| Charge | Utiliser Puppeteer | Utiliser gPdf |
|---|---|---|
| Rapport HTML existant -> PDF | Premier choix | Nécessite une réécriture |
| Factures, relevés, reçus | Marteau lourd | Premier choix |
| Étiquettes d’expédition avec codes-barres | À éviter, problèmes de polices | Premier choix |
| Facture électronique (Factur-X / ZUGFeRD / EN 16931) | Pas de prise en charge intégrée | Intégré |
| Archivage long terme PDF/A | Nécessite une passe Ghostscript | Profils intégrés |
| Maquettes design system fidèles au pixel | Premier choix | Mauvais outil |
| Graphiques qui nécessitent vraiment D3 / Recharts | Premier choix | Mauvais outil |
| Billets, certificats, badges nominatifs | Surdimensionné | Premier choix |
| Tout document qui a besoin de JavaScript au rendu | Seul choix | Mauvais outil |
Si la colonne de droite correspond à plus de trois lignes de votre cas, les économies ne sont pas marginales.
Comparaison réelle : rendu d’une facture d’une page
Même contenu. Même format papier. Mêmes polices, NotoSans. Même profil de sortie PDF/A-3b.
| Puppeteer (Lambda chaude, 1 GB) | gPdf (Cloudflare Worker chaud) | |
|---|---|---|
| Latence p50 | 180 ms | 3,4 ms |
| Latence p99 | 420 ms | 8 ms |
| Pénalité de démarrage à froid | +1 800 ms au premier rendu | +12 ms au premier rendu |
| Mémoire au pic | 720 MB | 18 MB |
| Taille image / module | 280 MB | 4,5 MB |
| Glyphes CJK | Non, sauf installation explicite | Oui, NotoSans CJK intégré |
| Coût / 100 000 rendus | ~240 USD, calcul Lambda | ~5 USD, plan gPdf Basic |
La dernière ligne surprend souvent. L’écart de coût est réel, et ce n’est pas un prix d’appel. Il est structurel. Nous n’avons pas à amortir le démarrage de Chromium, la mémoire du navigateur ou les démarrages à froid de conteneurs ; le coût unitaire par rendu est donc réellement minuscule.
“Mais 5 USD pour 100 000 pages paraît trop bon marché. Où est le piège ?”
Le piège, c’est précisément que nous n’expédions pas de navigateur. Le coût d’un moteur binaire sur isolate V8 chaud se compte en millisecondes de CPU et en kilo-octets de mémoire. Facturer au prix de Puppeteer reviendrait à facturer une infrastructure que nous n’exploitons pas.
Quand vous devriez quand même choisir Puppeteer
La réponse honnête n’est pas toujours “utilisez gPdf”. Voici les cas où Puppeteer reste le bon choix :
-
Vous exploitez déjà Puppeteer en production et il fonctionne. Ne migrez pas pour le plaisir. Le bon moment pour évaluer gPdf arrive quand Puppeteer commence à faire mal, typiquement quand les factures de calcul dépassent 400 USD/mois ou quand un SLA aval casse à cause du démarrage à froid.
-
Vos documents sont des pages web existantes, point final. Un rapport généré par l’utilisateur de 60 pages, stylé par votre design system, avec graphiques imbriqués et contenu dynamique, n’est pas une migration JSON. C’est une reconception.
-
Vous avez besoin d’une parité parfaite avec un aperçu web. Certains processus, par exemple “ce que vous voyez dans l’éditeur est ce qui s’imprime”, ont vraiment besoin que Chromium soit le moteur des deux côtés.
Si aucun de ces cas ne s’applique, le calcul est simple : déploiement plus petit, latence plus basse, facture plus basse, sortie identique octet pour octet, et moins de drames autour des polices.
Comment migrer une vraie charge
Si vous êtes assez convaincu pour essayer, la migration ressemble souvent à un spike de 1 à 2 jours par type de document, pas à une refonte d’architecture :
- Choisissez un document, en commençant par celui au plus fort volume, pas par le plus complexe.
- Mappez les sections logiques de votre modèle HTML vers les éléments JSON gPdf :
text,box,table,barcode,image. - Utilisez le Playground pour itérer sur un vrai
DocumentRequestjusqu’à obtenir une sortie correspondante. - Branchez votre forme de données existante dans une petite fonction d’adaptation qui émet le JSON.
- Testez le nouveau service en A/B face à votre service Puppeteer pendant une semaine. Comparez les PDF. Décidez.
La plupart des équipes comprennent le modèle JSON en moins d’une journée. Le plus difficile n’est pas le nouvel outil ; c’est de démêler les acrobaties HTML/CSS que l’ancien modèle a accumulées avec le temps.
TL;DR
Puppeteer est le bon choix pour les pages web. Pour les documents, vous payez une taxe de 100 à 200x à chaque rendu pour éviter le petit coût initial de décrire votre document comme des données. Si votre parc génère factures, étiquettes, reçus, relevés, billets ou tout autre document “de même forme à chaque fois, seules les valeurs changent”, un moteur à l’edge comme gPdf sera mesurablement plus rapide, plus petit, moins cher et plus déterministe.
Essayez-le dans le Playground : c’est un vrai Worker à l’edge, sans inscription, avec une réponse dans votre navigateur en moins de 5 ms.