Pourquoi auto-héberger une API de génération PDF ?
Les services PDF SaaS (Puppeteer Cloud, PDFShift, DocRaptor) facturent par document, par page ou par volume d'appels API. Dès que votre application génère des factures, rapports ou contrats à l'échelle, le coût augmente linéairement avec votre usage. Gotenberg les remplace par un coût VPS mensuel fixe, quel que soit le nombre de PDF générés. Il préserve également la confidentialité de vos documents : rien ne quitte votre infrastructure pendant la conversion.
Gotenberg, WeasyPrint, Stirling-PDF ou Puppeteer ?
Faites défiler le tableau
| Outil | Formats sources | RAM min | API REST | Licence |
|---|---|---|---|---|
| Gotenberg 8.x | HTML, URL, DOCX/ODT/XLSX, Markdown, images | 2 Go (Chromium) | Oui — multipart/form-data | MIT |
| WeasyPrint | HTML + CSS (pas JS) | 256 Mo | Non — lib Python/CLI | BSD |
| Stirling-PDF | PDF (édition, OCR, fusion) | 512 Mo | Oui — JSON/multipart | MIT (AGPLv3 pour Pro) |
| Puppeteer seul | HTML + URL via headless Chrome | 1,5 Go | Non — lib Node.js | Apache 2.0 |
Ce que Gotenberg peut convertir
- HTML vers PDF via une instance Chromium complète — CSS, polices web, JavaScript et media queries d'impression sont tous honorés.
- N'importe quelle URL vers PDF — archivez des vues de tableau de bord, générez des rapports hebdomadaires ou capturez des reçus de paiement.
- Documents LibreOffice (DOCX, XLSX, PPTX, ODT, ODS, ODP et 20+ formats) vers PDF sans Microsoft Office.
- Markdown vers PDF via la route Chromium avec CSS configurable.
- Plusieurs PDF fusionnés en un seul fichier en un appel API.
- Images (PNG, JPEG, WebP, TIFF) converties en PDF.
Prérequis
Un VPS avec au moins 1 vCPU et 2 Go de RAM (Chromium seul consomme 1,2 à 1,5 Go au rendu ; 1 Go de RAM est insuffisant dès qu'une conversion est en cours). Ubuntu 22.04 ou 24.04 avec Docker et Docker Compose v2 installés. Gotenberg écoute sur le port 3000 par défaut. Aucun domaine n'est nécessaire si vous appelez l'API depuis le même serveur — http://127.0.0.1:3000 suffit. Pour l'exposer à d'autres services, un reverse proxy nginx est recommandé (voir plus bas).
Déployer Gotenberg étape par étape
Créer le fichier compose
Connectez-vous en SSH à votre VPS et créez un répertoire :
mkdir gotenberg && cd gotenbergCréez
docker-compose.yml:services: gotenberg: image: gotenberg/gotenberg:8 restart: unless-stopped ports: - "127.0.0.1:3000:3000" command: - gotenberg - --chromium-disable-javascript=false - --api-timeout=30s - --chromium-max-queue-size=10La liaison à
127.0.0.1empêche l'accès direct depuis Internet. Au 2026, la version stable est 8.x (8.9.1 et supérieures) — l'image:8suit la dernière 8.x.Démarrer le conteneur
Exécutez
docker compose up -d. Gotenberg tire l'image (~1,5 Go, inclut Chromium et LibreOffice) et démarre le serveur HTTP. Vérifiez l'endpoint de santé :curl http://127.0.0.1:3000/healthUne réponse
{"status":"up"}confirme que le service est prêt.Générer votre premier PDF depuis HTML
Créez un fichier HTML minimal (
facture.html) et convertissez-le :curl -s --request POST http://127.0.0.1:3000/forms/chromium/convert/html \ --form '[email protected]' \ -o facture.pdfLa réponse est le PDF en flux binaire — redirigez-le vers un fichier, un objet réponse ou un upload S3.
Convertir un document Word en PDF
Téléversez un fichier DOCX vers la route LibreOffice :
curl -s --request POST http://127.0.0.1:3000/forms/libreoffice/convert \ --form '[email protected]' \ -o contrat.pdfGotenberg utilise le moteur LibreOffice intégré pour la conversion — aucune licence Microsoft Office requise.
Ajouter une authentification (recommandé)
Gotenberg n'a pas d'authentification intégrée. L'approche recommandée : installez nginx, créez un reverse proxy de
https://gotenberg.votre-domaine.comvershttp://127.0.0.1:3000, et ajoutez une authentification HTTP basic. Seul le backend de votre application devrait appeler cet endpoint — ne l'exposez jamais sans authentification à Internet public, car il peut être utilisé pour rendre des URLs arbitraires.
Routes et formats de l'API
Gotenberg expose deux familles de routes REST, toutes en POST multipart/form-data.
Routes Chromium — rendu via un navigateur headless :
- POST /forms/chromium/convert/html — conversion depuis un fichier HTML local (champ index.html) ; les assets CSS/JS/images peuvent être joints en champs supplémentaires.
- POST /forms/chromium/convert/url — conversion depuis une URL publique (champ url) ; Chromium charge la page entière, JavaScript compris.
- POST /forms/chromium/convert/markdown — conversion Markdown avec feuille de style optionnelle.
Routes LibreOffice — conversion de documents bureautiques :
- POST /forms/libreoffice/convert — accepte DOCX, XLSX, PPTX, ODT, ODS, ODP et 20+ formats ; plusieurs fichiers convertis en un seul PDF si vous envoyez merge=true.
Route PDF — manipulation de PDF existants :
- POST /forms/pdfengines/merge — fusionne plusieurs fichiers PDF en un seul.
- POST /forms/pdfengines/convert — conversion vers PDF/A (archivage long terme).
Webhook : au lieu de recevoir le PDF en réponse synchrone, vous pouvez passer un header Gotenberg-Webhook-Url — Gotenberg envoie alors le PDF en POST vers votre URL dès la conversion terminée, libérant la connexion cliente immédiatement. Utile pour les conversions longues ou les architectures asynchrones.
Reverse proxy nginx
Pour exposer Gotenberg à d'autres services ou l'appeler depuis un domaine dédié, ajoutez un bloc location dans votre configuration nginx :
server {
listen 443 ssl;
server_name gotenberg.votre-domaine.com;
# TLS via certbot ou votre CA
ssl_certificate /etc/letsencrypt/live/gotenberg.votre-domaine.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/gotenberg.votre-domaine.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 60s; # laisser le temps à Chromium de rendre
client_max_body_size 50M; # pour les gros DOCX/XLSX
# Authentification HTTP basic
auth_basic "Gotenberg API";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}Si Gotenberg tourne sur le même serveur que votre application backend, préférez l'appel direct sur http://127.0.0.1:3000 sans passer par nginx — moins de latence et pas besoin d'exposer le service sur un domaine.
Intégration avancée : PHP, Python, Node.js
Gotenberg est indépendant du langage — tout client HTTP capable d'envoyer du multipart/form-data fonctionne.
PHP / Laravel — utilisez le package officiel gotenberg-php :
use Gotenberg\Gotenberg;
use Gotenberg\Stream;
$request = Gotenberg::chromium('http://127.0.0.1:3000')
->pdf()
->html(Stream::path('/chemin/vers/facture.html'));
$response = $client->sendRequest($request);
file_put_contents('facture.pdf', $response->getBody());Python — avec la bibliothèque gotenberg-client :
from gotenberg_client import GotenbergClient
with GotenbergClient('http://127.0.0.1:3000') as client:
with client.chromium.html_to_pdf() as route:
route.index(Path('facture.html'))
response = route.run()
Path('facture.pdf').write_bytes(response.content)Node.js — avec fetch natif (Node 18+) :
const form = new FormData();
form.append('index.html', new Blob([htmlContent], { type: 'text/html' }), 'index.html');
const res = await fetch('http://127.0.0.1:3000/forms/chromium/convert/html', {
method: 'POST',
body: form,
});
const pdf = Buffer.from(await res.arrayBuffer());
fs.writeFileSync('facture.pdf', pdf);Pour Node.js, le package chromiumly encapsule l'API complète si vous préférez éviter le FormData manuel.
Configuration avancée
Les paramètres se passent en arguments de la commande gotenberg dans votre docker-compose.yml (ou via variables d'environnement préfixées GOTENBERG_).
Timeout : --api-timeout=30s — durée maximale d'une conversion. Augmentez-le (ex. 60s) si vous convertissez de longs documents DOCX ou des pages avec beaucoup de JavaScript.
Concurrence : --chromium-max-queue-size=10 — nombre maximum de conversions Chromium en file. Au-delà, Gotenberg répond 503 plutôt que d'accumuler en mémoire. Ajustez selon votre RAM disponible (chaque conversion Chromium ouvre une instance éphémère).
Rate limit : --api-port-from-env combiné à un reverse proxy nginx (limit_req_zone) constitue la solution recommandée pour limiter les appels entrants.
Logs : Gotenberg logue en JSON sur stdout — docker compose logs gotenberg vous donne le détail de chaque conversion (durée, route, statut). Pour un log plus verbeux :
command:
- gotenberg
- --log-level=debugDésactiver LibreOffice (si vous n'avez besoin que de Chromium, économie de RAM) :
command:
- gotenberg
- --libreoffice-disable-listener=truePour les architectures à fort volume, lancez plusieurs répliques Gotenberg derrière un équilibreur de charge nginx (upstream) : chaque instance tient ses propres workers Chromium, et vous scalez horizontalement sans partager d'état.
Dépannage des erreurs courantes
Timeout Chromium — context deadline exceeded : la conversion HTML dépasse le timeout configuré. Causes fréquentes : page avec JavaScript lent, ressources externes (CDN, polices Google Fonts) inaccessibles depuis le serveur, ou DOM trop volumineux. Solution : augmentez --api-timeout, hébergez vos assets localement et joignez-les en champs du formulaire multipart plutôt que de les charger par URL.
Port 3000 déjà occupé : une autre application écoute sur 3000. Modifiez le mapping dans docker-compose.yml : "127.0.0.1:3001:3000" et mettez à jour l'URL dans vos appels.
Fichier trop grand — 413 Request Entity Too Large : nginx coupe la requête avant Gotenberg. Augmentez client_max_body_size dans le bloc nginx (ex. 100M pour les gros XLSX).
Mémoire insuffisante — OOMKilled : Docker tue le conteneur quand Chromium dépasse la RAM disponible. Vérifiez docker inspect gotenberg | grep OOMKilled. Remèdes : ajouter de la RAM au VPS, limiter la concurrence (--chromium-max-queue-size=3), ou désactiver JavaScript si votre HTML ne le requiert pas (--chromium-disable-javascript=true).
Erreur LibreOffice — fichier corrompu ou format non reconnu : certains DOCX générés par des outils tiers ne sont pas conformes au standard OOXML. Vérifiez d'abord que le fichier s'ouvre sans erreur dans LibreOffice Desktop. L'en-tête de log Gotenberg indique le message LibreOffice exact (docker compose logs gotenberg --tail 50).
Intégrer avec votre application
Gotenberg est indépendant du langage — tout client HTTP fonctionne. Des bibliothèques existent pour PHP (gotenberg-php), Node.js (chromiumly), Python (gotenberg-client), Go et d'autres. Pour Laravel, un package officiel encapsule l'API. En Node.js, vous pouvez l'appeler avec axios ou l'API fetch native en envoyant un objet FormData avec le contenu HTML ou le fichier. Consultez la section « Intégration avancée » ci-dessus pour des exemples de code prêts à l'emploi dans chaque langage.