---
title: "PuppeteerでPDFを生成する方法｜page.pdfの設定とAWS Lambda（@sparticuz/chromium）での動かし方"
url: "https://www.issoh.co.jp/tech/details/2275/"
published: 2024-05-24
updated: 2026-10-31
categories: ["AWS"]
publisher: "株式会社一創"
---

# PuppeteerでPDFを生成する方法｜page.pdfの設定とAWS Lambda（@sparticuz/chromium）での動かし方

PuppeteerのPDF生成は`page.pdf()`を呼ぶだけで動きます。ただ、用紙が日本の帳票と合わない、改ページで表が崩れる、AWS Lambdaに載せたら起動しない、日本語が□になる、といった問題は、どれも既定値や版の組み合わせを知らないと避けられません。この記事では、`page.pdf()`の指定をpuppeteer-core 25.12.0とChrome 154で実測した結果とともに整理し、Lambdaで動かすときの版の合わせ方・サイズ上限・フォント配置を2026年10月時点の数値で示します。

## まとめ

- **用紙**：`format`の既定は`letter`です。A4の帳票なら`format: 'A4'`を必ず書きます。
- **戻り値**：v23.0.0以降`page.pdf()`は`Uint8Array`を返します。`pdf.toString('base64')`は例外を出さずに数値の羅列を返すので、`Buffer.from(pdf)`で包んでから変換します。
- **改ページ**：CSSの`break-before: page`が効き、`<thead>`の見出し行は各ページに繰り返されます。`outline: true`で見出しからしおりが作られます。
- **Lambdaの版**：`@sparticuz/chromium@153`には`puppeteer-core@25.11.0`を合わせます。最新の25.13.0はChrome 155向けです。
- **サイズ**：153.0.0の`chromium.br`は67.0MBで、コンソールからのZIPアップロード上限50MBを単体で超えます。実行時に展開される209.0MBは`/tmp`を消費します。
- **日本語**：Lambdaにはフォントが無く、同梱のOpen Sansに日本語は含まれません。`fonts`ディレクトリごとレイヤで持ち込みます。

## page.pdfでPDFを出力する基本コード

### URLを開いてPDF化する最小実装

`npm install puppeteer`で導入すると、対応するChromeが同時にダウンロードされます。PDF生成は次の数行で完結します。

```
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.pdf({ path: 'out.pdf', format: 'A4', printBackground: true });
await browser.close();
```

`format: 'A4'`を省くとLetter（8.5×11インチ）で出ます。[PDFOptionsの公式リファレンス](https://pptr.dev/api/puppeteer.pdfoptions)の既定値は`letter`で、実際に何も指定せず出力したPDFのページサイズは612×792pt、A4指定時は595.92×841.92ptでした。用紙指定のないサンプルを流用するとLetterで出力されるため、A4で印刷する帳票では生成したPDFの用紙サイズも確認してください。

ダウンロードされたブラウザはv19.0.0以降、`node_modules`ではなく`$HOME/.cache/puppeteer`に置かれます。プロジェクトをZIPに固めて別環境へ運んでもブラウザ本体は付いてきません。この性質が、後半で扱うLambdaの構成を決めます。

### HTML文字列から帳票を作るsetContent

請求書や明細書のように、サーバー側でHTMLを組み立ててPDFにする用途では`page.goto()`ではなく`page.setContent()`を使います。外部の画像やWebフォントを読み込む場合は、読み込みが終わるまで待つ条件を付けます。

```
const html = renderInvoice(data); // テンプレートエンジンで組み立てたHTML
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({ format: 'A4', printBackground: true });
```

`page.pdf()`は`waitForFonts`が既定で`true`のため、`document.fonts.ready`の解決を待ってから出力します。固定時間の待機は要りません。古い記事にある`page.waitForTimeout()`はv22.0.0（2024年2月5日）で削除されており、呼んだ時点でエラーになります。

### v23以降のUint8ArrayでBase64返却が壊れる条件

puppeteer-core v23.0.0（2024年8月7日）の破壊的変更「[use Uint8Array instead of Buffer for browser compatibility](https://github.com/puppeteer/puppeteer/releases/tag/puppeteer-core-v23.0.0)」で、`page.pdf()`の戻り値は`Promise<Uint8Array>`になりました。`Uint8Array`の`toString()`は引数を無視し、要素をカンマ区切りで並べた文字列を返します。

```
// %PDF-1.4 の先頭8バイトで確認（Node.js v26.5.0）
const u = new Uint8Array([0x25,0x50,0x44,0x46,0x2d,0x31,0x2e,0x34]);

u.toString('base64');              // '37,80,68,70,45,49,46,52'（引数が無視される）
Buffer.from(u).toString('base64'); // 'JVBERi0xLjQ='（正しい）
```

例外が出ないので、API GatewayへBase64で返す実装が残っていると、ログに何も出ないまま壊れたファイルが配信され続けます。v22以前のコードを移行するときは、Base64化のほかに`Buffer`専用メソッドを使っている箇所も洗い出してください。

## page.pdfの用紙・改ページ・しおりの設定

### printメディアと背景色が落ちる理由

画面とPDFの見た目が違う原因の多くはメディアタイプです。[page.pdf()の公式リファレンス](https://pptr.dev/api/puppeteer.page.pdf)にあるとおり、`page.pdf()`はprintのCSSメディアタイプで描画します。`@media print`で隠した要素はPDFにも出ません。画面と同じ見た目にしたいときは、出力前に`page.emulateMediaType('screen')`を呼びます。

背景色は2段構えです。`printBackground: true`で背景の描画を有効にし、印刷向けの色変換を止めたい要素にはCSSで`-webkit-print-color-adjust: exact`を指定します。片方だけでは色が出ない場合があるので、両方を確認してください。

### 用紙・余白・ページ番号の指定

| オプション               | 既定値    | 用途           |
| ------------------- | ------ | ------------ |
| format              | letter | A4などの用紙      |
| margin              | 未設定    | 上下左右の余白      |
| printBackground     | false  | 背景の描画        |
| landscape           | false  | 横向き          |
| scale               | 1      | 描画倍率         |
| displayHeaderFooter | false  | ヘッダー・フッター    |
| preferCSSPageSize   | false  | CSSの@pageを優先 |
| tagged              | true   | タグ付きPDF      |
| outline             | false  | しおり          |
| timeout             | 30000  | 出力の待ち時間（ms）  |

ページ番号は`displayHeaderFooter: true`と`footerTemplate`で入れます。テンプレート内では`pageNumber`と`totalPages`のクラスを持つ要素に値が差し込まれます。テンプレートは本文のCSSを引き継がないので、フォントサイズはテンプレート内に書きます。

```
await page.pdf({
  format: 'A4',
  margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
  displayHeaderFooter: true,
  headerTemplate: '<span></span>',
  footerTemplate:
    '<div style="font-size:9px;width:100%;text-align:center;">' +
    '<span class="pageNumber"></span> / <span class="totalPages"></span></div>',
});
```

フッターが本文に重なるときは、まず`margin.bottom`にフッターを収める余白があるかを確認します。用紙サイズをCSSの`@page`で管理しているなら、`format`ではなく`preferCSSPageSize: true`にすると二重管理を避けられます。

### CSSによる改ページと表ヘッダーの繰り返し

章の区切りで改ページしたいときは、CSSの[break-before](https://developer.mozilla.org/ja/docs/Web/CSS/break-before)に`page`を指定します。既定のスタイルのまま、h1見出し・2列120行の表・`break-before: page`を付けた2つ目のh1を持つHTMLを`setContent()`で渡し、A4で出力したところ、次の結果になりました（puppeteer-core 25.12.0、chrome-headless-shell 154.0.8037.57、macOS）。ページ数はフォントや行の高さで変わるので、結果は構造の確認として読んでください。

| 確認項目                    | 結果               |
| ----------------------- | ---------------- |
| 表の見出し行（thead）           | 表がかかる1〜4ページ目に再描画 |
| break-before: page の見出し | 5ページ目の先頭から開始     |
| outline: true           | h1の2件がしおりに入る     |
| tagged: false           | 構造ツリーが消える        |

表の見出し行を各ページに出すには、見出し行を`<thead>`に入れるだけで足ります。`<tbody>`の1行目を太字にして見出し代わりにしている帳票では繰り返されません。1件の明細を途中で分割させたくない場合は、その要素に`break-inside: avoid`を指定します。

### ログイン必須ページ・社内ページの認証とCSS取得

会員サイトや管理画面をPDFにするには、ログイン済みの状態を作ってから`page.goto()`します。フォームに入力してログインする方法と、Cookieを渡す方法があります。Cookieを渡す場合、v25の型定義では`page.setCookie()`が非推奨になっており、`browser.setCookie()`を使います。

```
await browser.setCookie({
  name: 'session',
  value: process.env.SESSION_ID,
  domain: 'admin.example.com',
});
const page = await browser.newPage();
await page.goto('https://admin.example.com/report', { waitUntil: 'networkidle0' });
```

Lambdaなど社外の実行環境から社内ネットワークのページを開くと、HTMLは渡せてもCSSや画像の取得に失敗し、スタイルの無いPDFになります。`setContent()`で渡すHTMLにCSSを`<style>`で埋め込み、画像はBase64のdata URLにしておけば、外部への通信なしで出力できます。

## AWS LambdaでPuppeteerを動かす構成

### @sparticuz/chromiumとpuppeteer-coreの組み合わせ

Lambdaでは、サーバーレス向けにビルドされたChromiumを[@sparticuz/chromium](https://github.com/Sparticuz/chromium)で調達し、Puppeteerはブラウザを同梱しない`puppeteer-core`を使います。READMEによると、このパッケージは特定のPuppeteer版に縛られず、Chromiumと展開処理、サーバーレス向けの起動引数だけを提供します。版はPuppeteer側の対応Chromeから逆算して選びます。

| puppeteer-core | 公開日        | 対応Chrome      |
| -------------- | ---------- | ------------- |
| 25.10.0        | 2026-09-03 | 152.0.7977.75 |
| 25.11.0        | 2026-09-14 | 153.0.8010.36 |
| 25.12.0        | 2026-09-23 | 154.0.8037.57 |
| 25.13.0        | 2026-10-08 | 155.0.8059.39 |

2026年10月12日時点の`@sparticuz/chromium`の最新は153.0.0（2026年9月11日公開）です。したがって**`@sparticuz/chromium@153`と`puppeteer-core@25.11.0`の組み合わせで固定**します。最新同士を入れると、Puppeteerは155を前提にCDPコマンドを送るため、差分に起因する不具合を踏む余地が残ります。対応表の一次情報はPuppeteerリポジトリの[versions.json](https://github.com/puppeteer/puppeteer/blob/main/versions.json)です。同じファイルの`lastMaintainedChromeVersion`（現在152.0.7977.75）は対応版ではなく、メンテナンス対象の最も古い版を指します。

`@sparticuz/chromium@153.0.0`の`engines`は`^22.17.0 || >=24.0.0`です。[Lambdaのランタイム一覧](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)では`nodejs20.x`の非推奨日が2026年4月30日に過ぎているので、`nodejs22.x`か`nodejs24.x`を選びます。

### ZIP・レイヤ・コンテナイメージの選び方

配置方法は、[Lambdaのクォータ](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html)から逆算して決まります。

| 項目                   | 上限                 |
| -------------------- | ------------------ |
| ZIP（API・SDK・コンソール経由） | 50MB               |
| ZIPの展開後（レイヤ込み）       | 250MB              |
| コンテナイメージ（非圧縮）        | 10GB               |
| 関数あたりのレイヤ            | 5個                 |
| /tmp                 | 512MB〜10,240MB     |
| 同期呼び出しのレスポンス         | 6MB（ストリーミングは200MB） |

`@sparticuz/chromium@153.0.0`の`bin/chromium.br`は67.0MBです。Brotliで圧縮済みのファイルはZIPにしてもほとんど縮まないので、このパッケージを含めたZIPはコンソールの50MB上限を超え、S3経由でのアップロードが必要になります。展開後250MBの上限で数えるのは、ZIPを展開した状態で残る`chromium.br`などの`bin`一式（npmの`unpackedSize`でパッケージ全体約70.1MB）と依存ファイル、レイヤの合計です。

Brotliを展開した後のChromium本体は209.0MBあり、こちらは実行時に`/tmp`へ書き出されます。149.0.0では61.8MBと190.6MBだったので、153.0.0までに実行時の展開サイズが約18MB増えています。

- **依存が軽い**：S3経由でZIPをアップロードする。Chromiumをレイヤに分けると、関数本体の更新のたびに大きなファイルを送らずに済む。
- **依存が重い・日本語フォントも入れる**：最初からコンテナイメージにして10GBの枠を使う。手順は[Go言語のLambda関数をコンテナイメージでデプロイする手順｜provided.al2023対応](/tech/details/4238/)の流れが応用できます。
- **arm64（Graviton）で動かす**：npmの`@sparticuz/chromium`はx64のバイナリしか含みません。`@sparticuz/chromium-min`を入れ、GitHubリリースの`chromium-VERSION-layer.arm64.zip`をレイヤにするか、`chromium-VERSION-pack.arm64.tar`をHTTPSで置いて`executablePath()`にURLを渡します。

版を上げたときは、配布ZIPを展開したファイルと全レイヤの合計が250MB以内かを確認します。レイヤに分けても合計の上限は増えません。日本語フォントや他の依存で250MBを超えるなら、コンテナイメージか`-min`パッケージによるバイナリの外部配置を選びます。

### Lambdaハンドラの実装とheadlessの指定

次の例は、JSONの`html`フィールドで受け取ったHTMLをPDFにして返します。API GatewayのREST APIでPDFをそのまま返すには、`isBase64Encoded: true`に加えて、APIの[バイナリメディアタイプ](https://docs.aws.amazon.com/apigateway/latest/developerguide/lambda-proxy-binary-media.html)の設定が必要です。ブラウザから呼ぶ場合はAcceptヘッダーの順序を制御できないため、公式は`*/*`を指定する方法を案内しています。

```
import puppeteer from 'puppeteer-core';
import chromium from '@sparticuz/chromium';

export const handler = async (event) => {
  const browser = await puppeteer.launch({
    args: await puppeteer.defaultArgs({ args: chromium.args, headless: 'shell' }),
    defaultViewport: { width: 1280, height: 800, deviceScaleFactor: 1 },
    executablePath: await chromium.executablePath(),
    headless: 'shell',
  });

  try {
    // API Gatewayのプロキシ統合では本文がevent.bodyに文字列で入る
    const input = typeof event.body === 'string'
      ? JSON.parse(event.isBase64Encoded
          ? Buffer.from(event.body, 'base64').toString('utf8')
          : event.body)
      : event;
    if (typeof input.html !== 'string') throw new Error('html must be a string');

    const page = await browser.newPage();
    await page.setContent(input.html, { waitUntil: 'networkidle0' });
    const pdf = await page.pdf({ format: 'A4', printBackground: true });

    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/pdf' },
      body: Buffer.from(pdf).toString('base64'),
      isBase64Encoded: true,
    };
  } finally {
    for (const p of await browser.pages()) await p.close();
    await browser.close();
  }
};
```

`headless: 'shell'`が要点です。Puppeteer v22以降、`headless: true`は通常のChromeと同等の新ヘッドレスモードを指し、旧来の軽量版は`chrome-headless-shell`として`'shell'`で指定します。`@sparticuz/chromium`はこの`headless_shell`を元にビルドされており、READMEも、このバイナリには新ヘッドレスモードが含まれていないようだと記しています。ローカルでは通常のChromeを使い、Lambdaでは`'shell'`に切り替える分岐を環境変数で持たせておきます。

`finally`でページとブラウザを閉じているのは、READMEが「Chromiumが想定より多くのページを開いて終了に失敗する」事例を挙げているためです。Puppeteerは一時プロファイルを`/tmp`に作り、ブラウザのプロセスが終了した時点で削除します。`browser.close()`に到達しないままウォームスタートで実行環境が使い回されると、残ったプロファイルが`/tmp`を圧迫し続けます。

Webpackやesbuildでバンドルする場合は、`@sparticuz/chromium`を外部依存（external）に指定します。バンドルに含めると`bin`ディレクトリが見つからず、`The input directory "/var/task/bin" does not exist`で失敗します。

### メモリ・タイムアウト・/tmpの設定値

READMEは割り当てメモリを最低512MB、1600MB以上を推奨としています。[Lambdaのメモリ設定](https://docs.aws.amazon.com/lambda/latest/dg/configuration-memory.html)はメモリに比例してCPUを割り当て、1,769MBで1vCPU相当です。Chromiumの描画はCPUを使うため、メモリを絞ると単価は下がっても実行時間が伸びます。1,769MB前後から始め、実行時間とコストを測って詰めてください。

タイムアウトが頻発するときは、上限を延ばす前に待機条件を疑います。`networkidle0`は通信が500ms途切れるまで待つので、解析タグや常時接続のあるページでは解決しないことがあります。`/tmp`は既定の512MBでも展開後209.0MBのChromiumは収まりますが、Puppeteerの一時プロファイルや生成したPDFも同じ領域を使うので、実サイズを測って広げます。

同期レスポンスの6MB上限には、Base64化で約33%膨らんだ本文とJSONの付帯情報が含まれます。PDF本体で使えるのは4.5MBより少し小さい値なので、余裕を持ってS3経由の返却へ切り替えます。ページ数が読めない帳票は、S3へ保存して署名付きURLを返す設計にしておくと上限を気にせずに済みます。料金とコールドスタートの考え方は[AWS Lambdaとは？仕組み・料金体系とコールドスタート対策・採用判断を実装者目線で解説](/tech/details/15407/)にまとめています。

## 日本語PDFが豆腐になる原因とフォントレイヤ

Lambdaで生成したPDFの日本語が□になるのは、設定ミスではなく既定の状態です。`@sparticuz/chromium`のREADMEは、Lambdaのランタイムにはフォントが1つも入っていないと明記しています。同梱されるのはOpen Sansだけで、対応はLatin・Greek・Cyrillicの3つです。ローカルで正しく出ていたのは、macOSやWindowsのフォントに暗黙に頼っていたためです。

レイヤで持ち込む場合は、日本語フォントを`fonts`という名前のディレクトリに入れてZIPにし、レイヤとして追加します。

```
mkdir fonts
cp NotoSansJP-Regular.ttf fonts/
zip -9 --filesync --move --recurse-paths fonts.zip fonts/
```

探索先は`bin/fonts.tar.br`内の`fonts.conf`で決まっており、`/var/task/.fonts`、`/var/task/fonts`、`/opt/fonts`、`/tmp/fonts`の4か所です。レイヤは`/opt`に展開されるので、`fonts/`ごとZIPにすれば`/opt/fonts`に一致します。READMEは`.fonts`も挙げていますが、同梱の`fonts.conf`が探す`.fonts`は関数パッケージ内の`/var/task/.fonts`だけで、レイヤ直下に置いた`/opt/.fonts`は探索されません。ディレクトリ名を`font`や`NotoSansJP`にした場合も同様に外れ、レイヤを付けても豆腐のままです。

CSSの`font-family`には持ち込んだフォント名を明示します。日本語フォントは1ウェイトで数MBあるため、展開後250MBの枠を使い切らないよう、必要なウェイトだけを入れてください。

## タグ付きPDFがLambdaで出ない理由

`tagged`の既定は`true`で、ローカルのChromeでは文書構造を持つPDFが出ます。前述の実測でも、既定の出力には構造ツリー（StructTreeRoot）が含まれ、`tagged: false`で消えました。

ところが`@sparticuz/chromium`は、PDFのタグ付けを有効にしないビルド設定で作られています。READMEは、アクセシブルなPDFが必要なら`enable_tagged_pdf = true`を加えてChromiumを自分でビルドし、`@sparticuz/chromium-min`と組み合わせるよう案内しています。スクリーンリーダー対応や公共調達の要件でタグ付きPDFが必要な場合は、既製の`@sparticuz/chromium`では満たせません。自前ビルドを持つか、コンテナイメージにディストリビューションのChromiumを入れる構成を検討してください。

## chrome-aws-lambdaから移行するときに動かなくなる箇所

日本語の解説には、いまも`chrome-aws-lambda`前提のコードが多く残っています。npmでの最新公開は10.1.0（2021年7月17日）で止まっており、`peerDependencies`も`puppeteer-core: ^10.1.0`のままです。GitHubリポジトリはアーカイブされていませんが、その後のChromiumとPuppeteerの版には追随していません。新規に組むなら`@sparticuz/chromium`を選びます。

`@sparticuz/chromium@153.0.0`のデフォルトエクスポートが持つのは`args`、`graphics`、`setGraphicsMode`、`executablePath()`の4つだけです。

| 旧 chrome-aws-lambda@10.1.0    | 現行での書き方                         |
| ----------------------------- | ------------------------------- |
| chromium.font(url)            | 廃止。fontsレイヤで配置                  |
| chromium.headless             | 廃止。launchに’shell’               |
| chromium.defaultViewport      | 廃止。launchで自前指定                  |
| chromium.puppeteer            | 廃止。puppeteer-coreをimport        |
| await chromium.executablePath | await chromium.executablePath() |

見落としやすいのは`executablePath`です。旧パッケージではプロパティだったため括弧なしで書けましたが、現行はメソッドです。括弧を付け忘れると関数オブジェクトがそのまま`launch()`に渡ります。

移行後は、出力サイズと処理時間をログに残してください。Base64の破損のように例外を伴わない失敗は、サイズの監視に加え、復号後の先頭が`%PDF-`で始まるかを確かめるだけでも検出できます。ログの設計は[AWS Lambdaのログ運用ガイド｜CloudWatch Logsの見方・JSON構造化・S3出力とコスト削減](/tech/details/4031/)、実行時間とメモリの監視は[AWS LambdaをCloudWatchで監視する方法｜標準メトリクス・ログ・アラーム・Lambda Insights](/tech/details/4028/)で扱っています。

## Puppeteerを使わないほうがよい場面

Puppeteerの強みは、画面用のHTMLとCSSをそのままPDFにできる点です。レイアウトが固定の帳票を大量に作る用途では、ブラウザを起動するコストが割に合わなくなります。JavaScriptを使わずHTMLとCSSだけで帳票を組む選択肢は、[WeasyPrintとは：HTMLとCSSで帳票PDFを作る使い方と日本語フォント・改ページの設定【2026年10月】](https://www.issoh.co.jp/tech/details/19135/)で整理しています。既存のwkhtmltopdfから移行する場合の判断は、[wkhtmltopdfとは：開発終了後の現状と日本語・改ページの対処、移行先の選び方](https://www.issoh.co.jp/tech/details/19133/)で整理しています。

ファインディの[支払明細書PDFの移行レポート](https://tech.findy.co.jp/entry/2026/06/03/070000)では、Lambda上のPuppeteerから、ブラウザを使わない`@react-pdf/renderer`へ移した結果、実行時間の中央値が約3,963msから約145msに、平均メモリが約912MBから約222MBに下がったと報告されています。移行前はChromiumの起動だけで5秒前後かかり、ハングしたプロセスの一時ファイルが`/tmp`に溜まってバッチが止まっていたとのことです。

- **Puppeteerを選ぶ**：既存の画面やWebページをそのままPDFにしたい。CSSのグリッドや複雑な表、グラフ描画のJavaScriptを使う。生成件数が少なく、1件ごとの待ち時間が問題にならない。
- **PDF専用ライブラリを選ぶ**：請求書のようにレイアウトが固定で、一括生成時のメモリ使用量やコールドスタートが処理期限・コストの制約になっている。

なお、ブラウザ自動化の他の手段との比較は[Browser Automationとは？ブラウザ操作を自動化する4つの方式と選び方【2026年】](/tech/details/9129/)、Puppeteerと同系統のライブラリとの違いは[Playwrightとは｜仕組み・できること・Seleniumとの違いを解説](/tech/details/3167/)を参照してください。Lambdaを含むAWSのPDF生成基盤を設計から任せたい場合は、[AWS・Google Cloud・Azureのインフラ構築支援](/service/system/aws/)でも相談を受け付けています。

## よくある質問

### PuppeteerのPDFがA4にならないのはなぜですか？

`format`の既定値が`letter`だからです。`page.pdf({ format: 'A4' })`と明示してください。CSSの`@page { size: A4; }`で管理している場合は、`preferCSSPageSize: true`を付けるとCSS側の指定が優先されます。

### AWS Lambdaでpuppeteerを使うと「Could not find Chrome」で失敗するのはなぜですか？

ブラウザ本体がデプロイパッケージに含まれていないためです。Lambdaでは`puppeteer-core`を使い、`@sparticuz/chromium`の`executablePath()`の戻り値を`launch()`に渡します。ローカルでも、pnpmのようにインストールスクリプトを既定で止めるパッケージマネージャーではブラウザのダウンロードが飛ばされるので、`npx puppeteer browsers install`を実行します。

### chrome-aws-lambdaは今も使えますか？

新規採用は避けてください。npmの最新は2021年7月17日公開の10.1.0で、`peerDependencies`は`puppeteer-core: ^10.1.0`のままです。後継の`@sparticuz/chromium`へ移り、`chromium.font()`と`chromium.headless`の箇所を書き換えます。

### Lambdaで生成したPDFの日本語が□になります。どうすればよいですか？

Lambdaのランタイムにはフォントが無く、同梱のOpen Sansは日本語に対応していません。Noto Sans JPなどを`fonts`ディレクトリに入れてZIPにし、レイヤとして追加します。レイヤで探索されるのは`/opt/fonts`なので、ZIP直下の名前は`fonts`にします。

### headless: trueとheadless: ‘shell’はどちらを使うべきですか？

Lambdaで`@sparticuz/chromium`を使うなら`'shell'`です。このバイナリは`headless_shell`が元で、新ヘッドレスモードを持ちません。ローカルの同梱Chromeでは`true`でも動くので、環境変数で切り替えます。

## 関連記事

- [Puppeteerとは？Chrome自動操作Nodeライブラリの仕組みと使い方【2026年最新】](/tech/details/3844/)
- [AWS Lambdaとは？仕組み・料金体系とコールドスタート対策・採用判断を実装者目線で解説](/tech/details/15407/)
- [Playwrightとは｜仕組み・できること・Seleniumとの違いを解説](/tech/details/3167/)
- [Go言語のLambda関数をコンテナイメージでデプロイする手順｜provided.al2023対応](/tech/details/4238/)
- [AWS Lambdaのログ運用ガイド｜CloudWatch Logsの見方・JSON構造化・S3出力とコスト削減](/tech/details/4031/)

---

出典: [PuppeteerでPDFを生成する方法｜page.pdfの設定とAWS Lambda（@sparticuz/chromium）での動かし方](<https://www.issoh.co.jp/tech/details/2275/>)（株式会社一創）
