開発

Gotenbergとは:DockerでHTMLとOffice文書をPDFにするAPIの使い方と帳票基盤の構成【2026年11月】

Gotenbergとは:DockerでHTMLとOffice文書をPDFにするAPIの使い方と帳票基盤の構成【2026年11月】

Gotenbergは、ChromiumとLibreOfficeをDockerコンテナに収め、HTTPのAPIとして呼べるようにしたPDF変換サーバーです。HTMLやMarkdownはChromiumで、WordやExcelはLibreOfficeでPDFにします。この記事では、2026年10月27日公開の8.39.0を前提に、イメージの選び方、HTMLとExcelをA4のPDFにするコード、ヘッダー・フッターと日本語フォント、本番の同時実行数とタイムアウト、外部通信の制限と認証、WeasyPrintやPuppeteerとの使い分けまでを実装者向けに整理します。

まとめ:Gotenbergは複数システムから呼ぶ共通のPDF変換API、外部公開は禁物

Gotenbergが向くのは、言語の違う複数の業務システムから、同じ方法で帳票PDFを出したい場面です。Chromiumやフォントを各アプリに抱えさせず、HTTPでファイルを送ればPDFが返ってきます。

押さえる点は3つです。インターネットに直接公開しないこと、利用者の入力を扱うなら外部通信の制限を有効にすること、LibreOfficeの変換は1件ずつしか進まない前提で台数を決めることです。

逆に、Pythonのアプリ1本から自社テンプレートの帳票を出すだけなら、サーバーを別に立てるGotenbergは手間が増えます。帳票の出力から保存・検索までを業務システムに組み込む場合は、文書管理システム開発として要件を一緒に整理すると手戻りを減らせます。

Gotenbergの仕組みと8.39系の現状、2つの変換エンジンとAPI

multipart/form-dataでファイルを送るとPDFが返るステートレスな構造

公式ドキュメントの概要は、Gotenbergを「文書をPDFに変換するDockerベースのAPI」と説明しています。ファイルをmultipart/form-dataで送るとPDFが返り、ChromiumやLibreOffice、フォントを利用側で管理する必要がありません。変換結果をサーバーに保存しない作りなので、台数を増やすだけで処理量を広げられます。

GitHubのリポジトリによると、本体はGo製で、ライセンスはMITです。業務システムに組み込んで販売しても、自社のソースを公開する義務は生じません。

8.39.0までの更新ペースと、8.31〜8.39で変わった外部通信の既定値

GitHubのReleasesでは、8.35.0(2026年8月7日)、8.36.0(8月14日)、8.37.0(9月11日)、8.38.0(10月15日)、8.39.0(10月27日)と、月に1回以上のペースで版が上がっています。直近の更新はセキュリティ修正が中心です。

版を上げる前に読むべきなのは、外部通信まわりの変更です。8.31.0で非公開IPへの通信を既定で拒否したものの、8.32.0で元の緩い既定値に戻りました。8.34.0ではLibreOfficeが文書内のリンク先を読み込まなくなり、8.35.0では環境変数のプロキシ設定を自動で引き継がなくなっています。リンクした画像が消えた、プロキシ経由の取得が失敗する、といった症状は、まずこの一覧と照らし合わせます。

変換・PDF操作・非同期の3系統に分かれるAPIのルート一覧

Routes Overviewに載っているルートは、用途で3系統に分かれます。帳票で使う頻度の高い順に並べると次のとおりです。

系統 主な機能 帳票での用途
Chromium HTMLの変換 テンプレートから組んだ帳票
LibreOffice Office文書の変換 Excel・Wordの帳票
PDF Engines 結合・暗号化 複数PDFの1冊化とパスワード
運用 死活監視・版の確認 監視と更新後の確認

HTMLの変換は/forms/chromium/convert/html、Office文書は/forms/libreoffice/convert、結合は/forms/pdfengines/merge、監視は/healthです。このほか、URLやMarkdownの変換、スクリーンショット、透かしやスタンプ、PDF/Aへの変換、電子インボイス規格Factur-Xの作成まで用意されています。帳票まわりの処理をGotenberg 1つに寄せられる点が、単体のライブラリとの違いです。

GotenbergをDockerで起動し、HTMLとExcelをA4のPDFに変換する手順

フル版・Chromium版・LibreOffice版のイメージと起動コマンド

Installationが示すイメージは、変換エンジンの組み合わせで分かれています。Chromium版はフル版より約30%、LibreOffice版は約40%小さくなります。

  • gotenberg/gotenberg:8:ChromiumとLibreOfficeの両方を含むフル版
  • gotenberg/gotenberg:8-chromium:HTML・URL・Markdownだけを変換する
  • gotenberg/gotenberg:8-libreoffice:Office文書だけを変換する
  • 8-cloudrun・8-aws-lambdaの付くもの:Cloud RunとAWS Lambda向け(Lambda版はBeta)
# 手元の検証用。127.0.0.1に限定して外部から届かないようにする
docker run --rm -p "127.0.0.1:3000:3000" gotenberg/gotenberg:8.39.0

# 起動確認
curl http://localhost:3000/health
curl http://localhost:3000/version

公式は、ポートを-p "3000:3000"で公開すると外部から届く状態になると注意しています。本番では:8のような可変タグより、8.39.0のように版を固定したタグを使うと、更新の時期を自分で決められます。

請求書のindex.htmlとロゴ画像をcurlとPythonから送る最小のコード

Convert HTML to PDFの決まりで、本文のHTMLはindex.htmlという名前で送ります。画像やCSSは同じリクエストに添え、HTML側からはファイル名だけで参照します。./assets/logo.pngのようなサブフォルダ指定は使えません。

curl --request POST http://localhost:3000/forms/chromium/convert/html \
  --form [email protected] \
  --form [email protected] \
  --form paperWidth=21cm --form paperHeight=29.7cm \
  --form marginTop=1.5cm --form marginBottom=1.5cm \
  --form printBackground=true \
  -o invoice.pdf

用紙の既定値はレターサイズ(8.5×11インチ)です。日本の帳票ではA4を明示し忘れると、下端が切れたり余白が変わったりします。業務アプリから呼ぶときは、テンプレートで組み立てたHTMLを文字列のまま送ります。

import requests

GOTENBERG = "http://gotenberg:3000"

def render_invoice_pdf(html: str, logo: bytes, invoice_no: str) -> bytes:
    files = [
        ("files", ("index.html", html.encode("utf-8"), "text/html")),
        ("files", ("logo.png", logo, "image/png")),
    ]
    data = {"paperWidth": "21cm", "paperHeight": "29.7cm", "printBackground": "true"}
    res = requests.post(
        f"{GOTENBERG}/forms/chromium/convert/html",
        files=files, data=data,
        headers={"Gotenberg-Output-Filename": f"invoice-{invoice_no}"},
        timeout=60,
    )
    res.raise_for_status()  # 400は入力の誤り、503は時間切れ
    return res.content

宛名などの入力値は、テンプレートエンジンの自動エスケープを通してからHTMLに埋め込みます。Gotenbergは受け取ったHTMLをそのまま描画するため、エスケープ漏れは帳票の改ざんにつながります。

Excelの見積書をLibreOfficeでPDF化し、1冊に結合する手順

Excelで作った見積書や検査成績書は、LibreOfficeの変換ルートへ送ります。対応する拡張子は128種類で、.xlsx・.docx・.pptxのほか.csvや.odtも受け付けます。

# シートごとに1ページへ収めてPDF化
curl --request POST http://localhost:3000/forms/libreoffice/convert \
  --form [email protected] \
  --form singlePageSheets=true \
  -o estimate.pdf

# 表紙と見積書を1つのPDFに結合(送った順に並ぶ)
curl --request POST http://localhost:3000/forms/pdfengines/merge \
  --form [email protected] \
  --form [email protected] \
  -o estimate_set.pdf

公式は、複雑な書式やSmartArtではMicrosoft Officeと見た目が多少ずれると注意しています。8.34.0以降は、文書からリンクしただけの画像は読み込まれず、変換は200のまま成功扱いになります。ロゴは文書に埋め込んでおくのが確実です。

帳票の見た目を決める用紙サイズ・ヘッダー・日本語フォントの指定と注意点

preferCssPageSizeでCSSの@pageを優先させる、用紙サイズの決め方

用紙サイズは、フォームのpaperWidth・paperHeightで渡す方法と、HTML側のCSSで@page { size: A4; }と書く方法の2通りがあります。CSSを正とする場合に送る指定は、preferCssPageSize=trueです。単位はin・pt・cmで、省略するとインチとして扱われます。

テンプレートを複数のチームで作るなら、CSS側に寄せるのがおすすめです。帳票ごとにAPIの引数を覚えなくて済み、A4縦と横の混在もCSSだけで切り替えられます。印刷時の背景色はprintBackground=trueを付けないと出ません。

header.htmlにページ番号を出す書き方と本文CSSが効かない制約

ヘッダーとフッターは、header.html・footer.htmlという別のファイルで送ります。どちらもhtml・head・bodyを備えた完全なHTMLにする決まりです。

<!DOCTYPE html>
<html><head><style>
  body { font-size: 8pt; width: 100%; text-align: center; }
</style></head>
<body><span class="pageNumber"></span> / <span class="totalPages"></span></body>
</html>

pageNumberとtotalPagesのクラスを付けた要素に、現在のページと総ページ数が入ります。ヘッダーとフッターは本文と別の場所で描画されるため、本文のCSSは効かず、JavaScriptも外部の画像も読み込まれません。ロゴを出すなら、Base64のデータURIで埋め込みます。

Noto CJKは同梱済み、ブランド書体を足すDockerfileと文字化けの切り分け

ConfigurationのFontsの節によると、公式イメージには日本語を含むfonts-noto-cjkが最初から入っています。WeasyPrintやwkhtmltopdfのように日本語フォントを自分で入れなくても、豆腐(□)にはなりません。一方、MS明朝などMicrosoftの書体はライセンスの都合で同梱されず、Excelの帳票はNotoやLiberationに置き換わって改行位置が変わります。

FROM gotenberg/gotenberg:8.39.0
USER root
# 自社のブランド書体を追加(ファイルは自社で利用許諾を確認したもの)
COPY fonts/*.ttf /usr/local/share/fonts/
USER gotenberg

コンテナは非rootのgotenbergユーザー(UID 1001)で動くため、追加の作業だけUSER rootに切り替え、最後に戻します。Dockerfileの書き方の基本はDockerfileの書き方と主要命令の解説にまとめています。

本番運用の構成、同時実行数・タイムアウト・Webhookによる非同期化

Chromiumは6並列、LibreOfficeは1件ずつの処理能力と台数

Configurationには、エンジンごとの処理能力の差が書かれています。Chromiumは1つのブラウザで最大6件を並行して処理し、既定の--chromium-max-concurrencyも6です。LibreOfficeは内部のロックのため並行処理ができず、1件ずつ変換します。

月末にExcelの請求書を数千件まとめて出すような業務では、LibreOfficeが詰まりの原因になります。公式が勧める対処は、Gotenbergのコンテナ数を増やすことです。HTMLの帳票はChromium版、Office文書はLibreOffice版と、イメージを分けて別々に台数を調整すると無駄がありません。メモリの目安は、Kubernetesで512Mi以上・0.2 CPU以上、Cloud Runでは1Gi以上が推奨値です。

API_TIMEOUTの30秒・再起動回数・キュー上限を決めるcomposeの設定例

主な設定は、起動時のフラグか同名の環境変数で変えられます。既定のリクエストの制限時間は30秒です。Chromiumは100回、LibreOfficeは10回の変換ごとに自動で再起動し、メモリの膨張を抑えます。

services:
  gotenberg:
    image: gotenberg/gotenberg:8.39.0
    # portsは公開せず、同じネットワークのアプリからgotenberg:3000で呼ぶ
    environment:
      API_TIMEOUT: "60s"                 # 既定30s。大きなExcelは延ばす
      CHROMIUM_MAX_QUEUE_SIZE: "50"      # 既定0(上限なし)
      LIBREOFFICE_MAX_QUEUE_SIZE: "20"
      LIBREOFFICE_AUTO_START: "true"     # 初回の変換待ちを無くす
      CHROMIUM_DENY_PRIVATE_IPS: "true"  # 次章の外部通信の制限
  app:
    build: .
    depends_on: [gotenberg]

キューに上限を設けると、溢れたリクエストはすぐにエラーで返ります。上限なしのままだと、待ち行列が伸び続けてアプリ側のタイムアウトが先に切れ、原因の切り分けが難しくなる状態です。時間切れは503、LibreOfficeのメモリ不足などは500で返るので、アプリ側は500だけを回数を決めて再試行します。

大量の帳票を204で受け付けて結果をWebhookで受け取る非同期の流れ

数百ページの報告書のように時間のかかる変換は、Webhookで非同期にします。リクエストにGotenberg-Webhook-Urlヘッダーを付けると、Gotenbergはすぐに204を返し、変換が終わったPDFを指定先へPOSTします。

失敗の通知先として、Gotenberg-Webhook-Events-UrlかGotenberg-Webhook-Error-Urlのどちらかが必須です。入力ファイルは、downloadFromフィールドでS3などのURLから取らせることもできます。受け取る側の署名検証や二重受信の扱いは、Webhookの作り方と受信側の実装で解説しています。

社内APIとして守る、外部通信の制限と認証・8.39.0への更新

既定は許可寄り、利用者の入力を扱うなら有効にするDENY_PRIVATE_IPS

Outbound URL Filteringは、既定の設定を「許可寄り」と明言しています。信頼できるネットワークの中で、自前のAPIゲートウェイの後ろに置く前提だからです。非公開IPへの通信を拒否するCHROMIUM_DENY_PRIVATE_IPS・WEBHOOK_DENY_PRIVATE_IPS・API_DOWNLOAD_FROM_DENY_PRIVATE_IPS・LIBREOFFICE_DENY_PRIVATE_IPSは、すべて既定でfalseです。

利用者が入力した文字列やURLが変換対象に混ざるなら、4つともtrueにします。そうしないと、HTML内の画像タグを足がかりに、クラウドのメタデータや社内の管理画面へリクエストが飛びます。この攻撃の仕組みを解説した記事が、SSRFの仕組みとクラウドメタデータ悪用の防御実装です。許可リストに一致したURLはIPの判定を素通りする仕様なので、正規表現は^で始めてホスト名の末尾まで書き切ります。

Basic認証の有効化と、8.39.0のセキュリティ修正を取り込む更新手順

同じネットワーク内でも、呼び出し元を絞るための手段は認証です。API_ENABLE_BASIC_AUTH=trueを設定し、ユーザー名とパスワードをGOTENBERG_API_BASIC_AUTH_USERNAME・GOTENBERG_API_BASIC_AUTH_PASSWORDで渡します。8.36.0からはOIDCのベアラートークン認証も選べます。

8.39.0のリリースノートでは、URLの変換ルートがhttp・https以外のスキームを400で拒否するようになり、侵害されたルート証明書が取り除かれました。版を固定したタグで運用し、リリースノートの「動作変更」の欄を読んでから8.39.0へ上げ、/versionの応答で反映を確かめる流れが安全です。

Gotenbergを採用する条件と、WeasyPrint・Puppeteerとの使い分け

帳票の作り方と呼び出し元の違いで決まる、PDF生成ツール4種の比較

HTMLやOffice文書からPDFを作る道具は、描画エンジンと呼び出し方で向き不向きが分かれます。

ツール 描画エンジン 呼び出し方 向く用途
Gotenberg 8.39系 Chromium・LibreOffice HTTP API 複数システムの共通基盤
WeasyPrint 70系 独自のCSS組版 Pythonの関数 Python単体の帳票
Puppeteer・Playwright Chromium Node.js等のコード JavaScriptで描く画面
wkhtmltopdf 0.12.6 Qt WebKit コマンド 既存資産の維持のみ

脚注や目次のページ番号など印刷用CSSの細かい組版は、WeasyPrintのほうが得意です。詳しくはWeasyPrintの使い方と日本語フォント・改ページの設定で整理しています。wkhtmltopdfからの移行を考えている場合はwkhtmltopdfの開発終了後の現状と移行先の選び方が出発点になります。Chromiumを自前のコードで操作する手順はPuppeteerのpage.pdfの設定とAWS Lambdaでの動かし方を参照してください。

採用してよいのは複数言語のシステムから呼ぶ場面、見送る3つの条件

判断を言い切ります。Gotenbergを採用してよいのは、JavaやPHP、Node.jsなど言語の違う複数のシステムから、HTMLとOffice文書の両方をPDFにしたい場合です。変換エンジンを1か所に寄せられ、Chromiumの更新もイメージの差し替えだけで済みます。

次の3つでは見送ります。1つ目は、Pythonのアプリ1本だけが帳票を出す場合で、WeasyPrintをライブラリとして呼ぶほうがサーバーを1台減らせます。2つ目は、Excelの帳票をMicrosoft Officeと寸分違わぬ見た目で出す必要がある場合です。LibreOfficeで描く以上、差は消えません。3つ目は、コンテナを常時動かせない小さな環境で月に数件しか出さない場合で、運用の手間が効果を上回ります。

帳票基盤を業務システムに組み込むときの設計範囲と、外注を考える目安

工数を大きく左右するのは、Gotenbergの導入作業よりもテンプレートの数と保存の要件です。帳票が数種類で社内システムからだけ呼ぶなら、composeの設定とクライアントのコードで足ります。数十種類のテンプレートを複数の部署で持ち、電子帳簿保存法に沿った保存や取引先への配信まで絡むなら、出力・配信・保存をまとめて設計し直す時期です。

進め方は帳票電子化の進め方:出力・配信・保存の順序と電子帳簿保存法の要件で整理しています。一創では、帳票テンプレートの作成からPDF変換基盤、保存・検索の仕組みまでを文書管理システム開発として受託しています。

よくある質問

Gotenbergの導入や帳票づくりで寄せられやすい質問をまとめました。

Gotenbergは無料で商用利用できますか?

できます。GotenbergのライセンスはMITで、利用料はかかりません。自社の業務システムに組み込んで販売しても、ソースコードを公開する義務はありません。条件は、配布物に著作権表示とライセンス文を含めることです。コンテナの中にはChromiumやLibreOffice、各種フォントが入っており、それぞれ別のライセンスを持ちます。イメージを社外へ配布する場合は、同梱物のライセンス表示もあわせて確認してください。

GotenbergでExcelやWordをPDFに変換するにはどうすればよいですか?

フル版かLibreOffice版のイメージを起動し、/forms/libreoffice/convertへファイルをfilesフィールドで送ります。Excelでシートごとに1ページへ収めたい場合に付ける指定は、singlePageSheets=trueです。複数のファイルを送ると、結果はZIPでまとめて返ります。1つのPDFにしたいならmerge=trueを指定してください。

Gotenbergで日本語が文字化けすることはありますか?

公式イメージにはfonts-noto-cjkが同梱されているため、日本語が□になることは通常ありません。意味の通らない記号に化ける場合は文字コードの問題なので、HTMLの先頭に<meta charset="utf-8">を書きます。MS明朝などを指定した帳票は、別のフォントに置き換わって改行位置やページ数が変わるので、使用許諾を確認したフォントをイメージに追加します。

Gotenbergでヘッダーやフッターにページ番号を入れる方法は?

HTML変換のリクエストにheader.htmlとfooter.htmlを添えて送ります。どちらもhtml・head・bodyを持つ完全なHTMLにし、class="pageNumber"とclass="totalPages"を付けた要素を置くと、ページ番号と総ページ数が入ります。ヘッダーとフッターには本文のCSSが効かないため、スタイルの記述先は各ファイルの中です。

GotenbergはAWS LambdaやCloud Runで動かせますか?

動かせます。公式は8-cloudrunと8-aws-lambdaの付いたイメージを配布しています。Cloud Runで推奨されるメモリ容量は、1Gi以上です。Lambda版はBeta扱いで、応答の上限は6MBです。大きなPDFを返す帳票では、Webhookで結果を別の場所へ送る構成を検討してください。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.29 テックブログ ソニー生命の不正アクセス:38.5万件の対象確認と窓口、停止中の手続き
  2. 2026.10.31 テックブログ 無印良品の情報漏洩:MUJIアプリ最大2,726万件の対象確認となりすましメール対策
  3. 2026.01.30 コラム 税務調査は個人と法人で何が違う?担当部署・件数・調べられる点と追徴税額を比較【令和6事務年度】
  4. 2026.10.31 テックブログ 松屋の不正アクセス:237万件の対象確認と窓口、漏れたパスワードの守り方
  5. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処

RELATED POSTS 関連記事

目次