HTML to PDF変換の全手法を2026年版で比較:pros・cons・コード付き
HTMLをPDFに変換するツールは多数存在します。「解決済みの問題」と言えますが、ユースケースに合ったツールを選ぶのは単純ではありません。各手法はレンダリング精度・CSSサポート・運用複雑性・コストで異なるトレードオフをしているからです。
この記事では2026年時点で利用可能な主要なHTML→PDF変換手法を、正直なpros/consと実際のコードとともに紹介します。
PuppeteerとマネージドAPIの比較に特化した記事はPuppeteer vs PDF APIをご覧ください。Chromiumベースの手法すべてに共通するCSSのコツはHTML to PDF CSSのコツをご覧ください。
各手法の概要
| 手法 | エンジン | CSSサポート | 運用複雑性 | セルフホスト |
|---|---|---|---|---|
| ブラウザの印刷ダイアログ | Chromium / WebKit | 完全 | なし(手動) | — |
| wkhtmltopdf | QtWebKit(アーカイブ済み) | 部分的 | 低 | 要 |
| Puppeteer / Playwright | Chromium | 完全 | 高 | 要 |
| WeasyPrint | Python独自レンダラー | 良好 | 低〜中 | 要 |
| Gotenberg | Chromium + LibreOffice | 完全 | 中 | 要 |
| マネージドPDF API | Chromium | 完全 | 非常に低 | 不要 |
1. ブラウザの印刷ダイアログ
最もシンプルな方法です。JavaScriptでwindow.print()を呼ぶと、ブラウザのネイティブ印刷ダイアログが開き、ユーザーがPDFとして保存できます。
// 印刷ダイアログを表示
window.print();
// 印刷ボタンに設定
document.getElementById('print-btn').addEventListener('click', () => {
window.print();
});
CSS @media print ルールでレイアウトを制御します。
@media print {
.no-print { display: none; }
body { font-size: 12pt; }
@page { margin: 20mm; size: A4; }
}
Pros:
- セットアップ不要・コスト不要
- 完全なブラウザレンダリングエンジンを使用
- サーバーサイドのコード不要
Cons:
- ユーザーが手動で保存する必要がある(自動化不可)
- 印刷ダイアログの外観はブラウザとOSによって異なる
- ページブレークの制御が難しい
- ヘッダー・フッター・複数ページのレイアウトが困難
- サーバーから無音でトリガーできない
最適なユースケース: 「PDFとして保存」ボタンで十分で、自動化が不要な開発者ツールや社内ダッシュボード。
2. wkhtmltopdf
QtWebKitレンダリングエンジンに基づくコマンドラインツール。長年にわたりサーバーサイドのHTML→PDF変換の主流でした。
# インストール(Debian/Ubuntu)
sudo apt-get install wkhtmltopdf
# 基本的な変換
wkhtmltopdf input.html output.pdf
# オプション付き
wkhtmltopdf \
--page-size A4 \
--margin-top 20mm \
--margin-bottom 20mm \
--margin-left 15mm \
--margin-right 15mm \
--encoding utf-8 \
invoice.html invoice.pdf
# Pythonラッパー
import subprocess
def html_to_pdf(html_path: str, output_path: str) -> None:
subprocess.run([
'wkhtmltopdf',
'--page-size', 'A4',
'--margin-top', '20mm',
'--margin-bottom', '20mm',
html_path,
output_path,
], check=True)
Pros:
- 軽量で高速(Chromiumベースのツールよりリソース消費が少ない)
- シンプルなCLIインターフェース
- 成熟しており、ドキュメントが豊富
Cons:
- 2023年にアーカイブ済み — セキュリティパッチやバグ修正がない
- QtWebKitエンジンはモダンブラウザより7〜8年遅れている
- CSS GridとFlexboxのサポートが不完全
- JavaScript実行が必要なページをレンダリングできない
- CJKフォントのサポートには手動でのフォントインストールが必要
- 新規プロジェクトには非推奨
最適なユースケース: まだ移行していないwkhtmltopdf使用中のレガシーシステム。移行ガイドはwkhtmltopdf移行ガイドをご覧ください。
3. Puppeteer(Node.js)
Puppeteerはheadless Chromiumを操作するNode.jsライブラリです。もともとブラウザ自動化とテスト向けに作られており、PDF生成はその機能の一つです。
const puppeteer = require('puppeteer');
async function generatePdf(html) {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
});
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
printBackground: true,
});
await browser.close();
return pdf;
}
Pros:
- モダンなCSS完全サポート(Grid・Flexbox・カスタムプロパティ)
- PDF生成前にJavaScriptを実行できる
- 大きなコミュニティとエコシステム
- Chromeと同一のレンダリング
Cons:
- Chromiumのインストールとバージョン管理が必要
- プロセスあたり200〜500MBのメモリ消費
- コールドスタート: 1〜3秒(Lambda上では8〜15秒)
- 同時処理にはブラウザプールが必要
- Dockerイメージが1GB以上に膨張
- PDF生成専用ではない
最適なユースケース: ブラウザテストでPuppeteerを既に使用していて低〜中規模のPDF生成が必要な場合、またはエクスポート前にpage.evaluate()でJavaScriptを実行する必要がある場合。
4. Playwright(Node.js・Python・.NET・Java)
PlaywrightはMicrosoftのブラウザ自動化ライブラリです。Chromium・Firefox・WebKitをサポートし、PuppeteerよりクリーンなAPIを提供します。PDF生成ではChromiumを使うため、出力はPuppeteerと同一です。
// Node.js
const { chromium } = require('playwright');
async function generatePdf(html) {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.setContent(html, { waitUntil: 'networkidle' });
const pdf = await page.pdf({
format: 'A4',
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
printBackground: true,
});
await browser.close();
return pdf;
}
# Python
import asyncio
from playwright.async_api import async_playwright
async def generate_pdf(html: str) -> bytes:
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.set_content(html, wait_until='networkidle')
pdf = await page.pdf(
format='A4',
margin={'top': '20mm', 'bottom': '20mm',
'left': '15mm', 'right': '15mm'},
print_background=True,
)
await browser.close()
return pdf
Pros:
- PuppeteerよりクリーンなAPI
- 多言語サポート(Node.js・Python・.NET・Java)
- 活発な開発・メンテナンス
- モダンなCSS完全サポート
Cons:
- Puppeteerと同じ本質的な問題: Chromium管理・200〜500MBメモリ・コールドスタート
playwright install chromiumでPuppeteerより大きなChromiumをダウンロード- PDF生成専用ではない
最適なユースケース: PuppeteerよりPythonや.NETを好む場合、またはテストでFirefox/WebKitのレンダリングが必要な場合。運用上の問題が痛点なら、マネージドAPIへの移行が費用対効果が高い選択肢です。PlaywrightからPDF API移行ガイドをご覧ください。
5. WeasyPrint
WeasyPrintは独自のレンダリングエンジン(Chromiumではない)を使ってHTMLとCSSをPDFに変換するPythonライブラリです。CSS Paged Media仕様を直接実装しています。
# インストール
pip install weasyprint
# CLI
weasyprint input.html output.pdf
from weasyprint import HTML, CSS
def generate_pdf(html: str, css: str = None) -> bytes:
html_doc = HTML(string=html)
stylesheets = [CSS(string=css)] if css else []
return html_doc.write_pdf(stylesheets=stylesheets)
WeasyPrintはCSS Paged Mediaの機能(@pageルール・名前付きページ・ランニング要素)のサポートが優れています。
@page {
size: A4;
margin: 20mm;
@bottom-center {
content: "Page " counter(page) " of " counter(pages);
}
}
/* ランニングヘッダー/フッター */
.header { position: running(header); }
@page { @top-center { content: element(header); } }
Pros:
- CSS Paged Mediaのサポートが優秀(ヘッダー・フッター・ページカウンター)
- 軽量 — Chromium不要
- 純粋なPython — Django・Flask・FastAPIへの統合が容易
- 複雑な複数ページレイアウトのドキュメントに強い
Cons:
- JavaScriptの実行をサポートしない
- CSSサポートはブラウザと同一ではない(一部のモダンプロパティが未対応)
- CJKフォントサポートにはフォントのインストールが必要
- 多くのCSSルールを持つ複雑なページではChromiumより遅い
最適なユースケース: CSS Paged Mediaの機能が重要で、JavaScriptレンダリングが不要な構造化ドキュメント(レポート・書類・契約書)を生成するPythonアプリケーション。
6. Gotenberg
GotenbergはChromiumとLibreOfficeをラップして文書変換用のREST APIを公開するオープンソースのDockerサービスです。
# DockerでGotenbergを起動
docker run --rm -p 3000:3000 gotenberg/gotenberg:8
# HTMLをPDFに変換
curl \
--request POST http://localhost:3000/forms/chromium/convert/html \
--form files=@index.html \
--form marginTop=20 \
--form marginBottom=20 \
-o output.pdf
import requests
def generate_pdf_gotenberg(html: str) -> bytes:
response = requests.post(
'http://localhost:3000/forms/chromium/convert/html',
files={'files': ('index.html', html, 'text/html')},
data={'marginTop': '20', 'marginBottom': '20'},
)
response.raise_for_status()
return response.content
Pros:
- 言語に依存しないREST API
- Chromiumの完全なレンダリング機能
- Word・Excel・PowerPointのPDF変換もサポート(LibreOffice経由)
- ローカル開発はDockerで一発起動
Cons:
- Dockerが必要 — サーバーレス環境での実行が困難
- スケーリングを自前で管理(コンテナの起動・スケールアウト)
- 大きなDockerイメージ(Chromium + LibreOffice)
- CJKフォントサポートには追加のDocker設定が必要
最適なユースケース: 言語に依存しないセルフホストのREST APIが必要で、Officeドキュメントの変換も必要なチーム。スケーリングを管理するKubernetesデプロイ。サーバーレスには不向き。
7. マネージドPDF API
マネージドPDF API(例:FUNBREW PDF)はホスト型サービスです。HTMLをPOSTするとPDFバイトが返ってきます。サーバーインフラの管理は不要です。
// Node.js
async function generatePdf(html) {
const response = await fetch('https://pdf.funbrew.cloud/api/v1/pdf/generate', {
method: 'POST',
headers: {
'X-API-Key': process.env.FUNBREW_PDF_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html,
options: {
format: 'A4',
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
print_background: true,
},
}),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return Buffer.from(await response.arrayBuffer());
}
# Python
import os, requests
def generate_pdf(html: str) -> bytes:
resp = requests.post(
'https://pdf.funbrew.cloud/api/v1/pdf/generate',
headers={
'X-API-Key': os.environ['FUNBREW_PDF_API_KEY'],
'Content-Type': 'application/json',
},
json={
'html': html,
'options': {
'format': 'A4',
'margin': {'top': '20mm', 'bottom': '20mm',
'left': '15mm', 'right': '15mm'},
'print_background': True,
},
},
timeout=30,
)
resp.raise_for_status()
return resp.content
Pros:
- インフラ管理ゼロ — APIキーだけで開始
- Chromiumと同等のレンダリング(Puppeteerと同じCSSサポート)
- 同時リクエストの自動スケーリング
- 自サーバー側にコールドスタートなし
- 日本語・CJKフォントを標準搭載
- 小さなDockerイメージ(自サーバーにChromiumが不要)
- Webhook・テンプレートエンジン・メール配信を内蔵
Cons:
- 大規模ではリクエスト単位のコストが発生
- 外部サービスへの依存
- HTMLをサードパーティのサーバーに送信する(データポリシーの確認が必要)
最適なユースケース: PDF生成が重要な機能になっているほとんどの本番アプリケーション、サーバーレス環境、Chromiumインフラを維持したくないチーム。Playgroundでレンダリング品質を確認してから導入を検討してください。
手法の選び方
判断基準
自動化が必要か?
不要 → ブラウザの印刷ダイアログ
エクスポート前にJavaScriptの実行が必要か?
要 → Puppeteer・Playwright・またはマネージドAPI
主要言語は?
Python(JS不要) → WeasyPrint またはマネージドAPI
Python(JS必要) → Playwright(Python)またはマネージドAPI
Node.js → Puppeteer・Playwright・またはマネージドAPI
PHP / Ruby / Go → マネージドAPI またはGotenberg
Officeファイルの変換も必要か?
要 → Gotenberg
サーバーレス(Lambda・Vercel・CF Workers)か?
要 → マネージドAPIを強く推奨
月30件未満か?
要 → マネージドAPIの無料枠 またはPuppeteer
外部へのHTML送信を禁止するポリシーがあるか?
要 → Puppeteer・Playwright・またはGotenbergのセルフホスト
インフラ管理をゼロにしたいか?
要 → マネージドAPI
まとめ表
| 手法 | セットアップ | メモリ | サーバーレス | CJK | コスト |
|---|---|---|---|---|---|
| ブラウザ印刷 | なし | — | N/A | ネイティブ | 無料 |
| wkhtmltopdf | 低 | 低 | 困難 | 手動 | 無料(非推奨) |
| Puppeteer | 高 | 200〜500MB | 困難 | 手動 | サーバーコスト |
| Playwright | 高 | 200〜500MB | 困難 | 手動 | サーバーコスト |
| WeasyPrint | 中 | 低 | 容易 | 手動 | 無料 |
| Gotenberg | Docker | 高 | 困難 | Docker設定 | 無料(セルフホスト) |
| マネージドAPI | なし | < 10MB | 容易 | 標準搭載 | リクエスト単位 |
まとめ
2026年時点では、新規の本番アプリケーションにはマネージドPDF APIがデフォルトの推奨です。Puppeteerと同等のChromiumレンダリング品質を、運用負荷なしで提供します。セルフホストを選ぶ強い理由は、データ所在地の要件か、サーバーサイドレンダリングに移せないJavaScript実行が必要な場合に限られます。
既にPuppeteerやwkhtmltopdfを規模で運用しているチームには、メンテナンスエンジニアリング時間を考慮すると、マネージドAPIの方がほぼすべての現実的な規模でコスト効率が高くなります。Playgroundでテンプレートのレンダリング品質をテストし、無料プランを確認してから判断してください。
関連リンク
- Puppeteer vs PDF API:パフォーマンス・コスト比較 — PuppeteerとマネージドAPIの集中比較
- HTML to PDF API比較 2026年版 — 主要ツールのコード例付き比較
- wkhtmltopdf移行ガイド — wkhtmltopdfからモダンなソリューションへの移行
- HTML to PDF CSSのコツ — すべてのChromiumベース手法に共通するCSSテクニック
- 言語別APIクイックスタート — Node.js・Python・PHP・Ruby・GoのAPIコード
- PuppeteerからPDF API移行ガイド — Puppeteerの段階的な移行手順
- サーバーレス向けPDF APIガイド — Lambda・Cloudflare Workersデプロイ