ScreenshotNeo

BlogHTML to image & PDF

Convert a Webpage to PDF Using Chrome DevTools Protocol

Use Chrome DevTools Protocol’s Page.printToPDF to create a PDF from a rendered webpage, configure print output, and save the returned bytes or stream.

By the ScreenshotNeo team4 October 20267 min read

To convert a webpage to PDF using Chrome DevTools Protocol, navigate a Chrome page, wait for the content you need to be ready, and call Page.printToPDF in the Page domain. By default, the response contains base64-encoded PDF data; you can also request stream transfer and read the returned stream handle with the CDP client’s IO support. The protocol command exposes paper geometry, margins, orientation, page ranges, backgrounds, headers and footers, CSS paper sizing, tagged-PDF generation, and document-outline options. CDP Page.printToPDF reference.

1. What Page.printToPDF does

Page.printToPDF asks Chrome to print the current page and returns a PDF representation. It is a browser protocol command, so CDP handles the print operation while your application is responsible for browser startup or attachment, navigation, page readiness, and saving the result.

A navigation event alone does not prove that all content is ready. A site may still be loading images, rendering client-side content, or waiting on data. Choose readiness signals that fit the page you control: for example, wait for a known selector or for application code to indicate that rendering is complete. There is no universal wait condition that guarantees every website has finished.

2. Runnable example with Puppeteer and CDP

Puppeteer is a convenient Node.js client for launching Chrome and opening a CDP session. This example calls Page.printToPDF directly and writes the returned base64 data to a file. It waits for the document load event and then for a page-specific selector; change the selector to something that signals readiness for your target.

import puppeteer from 'puppeteer';
import { writeFile } from 'node:fs/promises';

const url = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch({ headless: true });

try {
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: 'load', timeout: 60_000 });
  await page.waitForSelector('main', { timeout: 30_000 });

  const session = await page.createCDPSession();
  await session.send('Page.enable');

  const result = await session.send('Page.printToPDF', {
    printBackground: true,
    landscape: false,
    paperWidth: 8.27,
    paperHeight: 11.7,
    marginTop: 0.4,
    marginBottom: 0.4,
    marginLeft: 0.4,
    marginRight: 0.4,
    preferCSSPageSize: true,
    transferMode: 'ReturnAsBase64',
  });

  await writeFile('page.pdf', Buffer.from(result.data, 'base64'));
  await session.detach();
} finally {
  await browser.close();
}

Install Puppeteer in a project with npm install puppeteer, save the code as an ES module such as print.mjs, and run node print.mjs https://example.com. Puppeteer manages the browser process and CDP transport here; the print command itself is the protocol method.

3. CDP settings and print layout

Use the settings deliberately because page styles and content differ. The protocol schema is the authority for the exact command fields and supported values.

Setting What it controls When to adjust it
landscape Page orientation. Use landscape for content designed to span wider pages; inspect pagination because orientation alone does not guarantee readability.
scale Print scaling. Adjust when content is clipped or too small, checking the resulting page breaks.
paperWidth, paperHeight Paper dimensions in inches. Specify dimensions for a fixed paper format. If CSS @page sizing should take precedence, consider preferCSSPageSize.
marginTop, marginBottom, marginLeft, marginRight Margins in inches. Set margins to reserve space around printed content or headers and footers.
pageRanges Pages to include, expressed as a range string. Use when only selected pages are required. Decide how your application handles invalid ranges with ignoreInvalidPageRanges.
printBackground Whether to print background graphics. Enable when colored blocks or background images carry meaning; otherwise the print stylesheet may omit them.
displayHeaderFooter, headerTemplate, footerTemplate Optional header and footer HTML. Enable and supply templates when each page needs running metadata. Templates can use documented substitutions for date, title, URL, page number, and total pages.
preferCSSPageSize Whether CSS-defined page size is preferred. Use when the page’s @page rules should determine the paper size; otherwise set dimensions explicitly for predictable output.
transferMode How PDF data is returned. Use base64 for a straightforward response or stream transfer when your CDP client can read protocol IO streams.
generateTaggedPDF, generateDocumentOutline Requests tagged-PDF generation and a document outline. Enable when these document structures are useful to your downstream workflow. The option alone does not establish a particular accessibility conformance level.

Header and footer templates accept HTML, with documented substitutions including date, title, url, pageNumber, and totalPages. Keep templates small and validate the printed result; the available source describes the substitutions but does not promise a particular layout for every page.

4. Receiving PDF data or a stream

The default transfer mode returns base64-encoded PDF data in the command response. Decode it to bytes before writing a file or sending it to storage. For large outputs, transferMode: 'ReturnAsStream' returns a stream handle instead; read it through the IO mechanism exposed by your CDP binding and close the stream when finished. Exact calls vary by client, so consult that client’s stream API as well as the protocol schema. Do not treat a stream handle as PDF bytes.

5. Readiness, print CSS, and page content

Chrome prints the rendered page using print behavior. CSS can change what is visible, how elements break across pages, and the page dimensions. In Puppeteer, page.pdf() uses the print CSS media type by default; emulate screen media before generating a PDF if the screen styles are desired. Puppeteer page.pdf documentation.

  • Wait for an application-specific readiness marker when the site renders content after navigation.
  • If images or other assets matter, wait for the page’s own loading behavior or verify the required assets before printing.
  • Review the page’s print stylesheet and @page rules; do not assume screen layout will carry over unchanged.
  • For pages with lazy content, determine how that page triggers loading before printing. The protocol method does not by itself guarantee that offscreen content has loaded.
  • Check page breaks, scale, margins, and orientation using representative pages from the target site.

6. Alternative: Puppeteer’s PDF method

If your application already uses Puppeteer and does not need to issue the raw CDP command, its page.pdf() API is a higher-level option that returns PDF bytes. It uses print media by default, and Puppeteer documents how to emulate screen media when needed. Print colors can be controlled with CSS such as -webkit-print-color-adjust. Puppeteer’s options are its library API; do not assume they match every raw protocol field one-for-one. Puppeteer PDF API.

7. Troubleshooting

Symptom Likely cause What to do
PDF is blank or missing page content Printing began before client-rendered content was ready, or the selected print styles hide it. Wait for a page-specific readiness signal and inspect the page under print media.
Images or late content are absent Assets had not loaded or lazy content had not been triggered. Wait for the relevant assets or page behavior explicitly; do not rely on navigation completion alone.
Output is clipped or too small Paper dimensions, orientation, scale, margins, or CSS page rules do not fit the content. Review those settings together, then inspect pagination and readability.
Background colors or images are missing Background printing is disabled or print CSS suppresses them. Set printBackground: true and check the print stylesheet.
Header or footer is absent displayHeaderFooter is not enabled, templates are missing, or the reserved layout is unsuitable. Enable the setting, provide the template, and tune margins while reviewing the PDF.
Page range fails or produces unexpected pages The range is invalid or exceeds the document’s page count. Use a valid range and choose the intended behavior for invalid ranges.
Client cannot decode the result The response is base64 data, not raw PDF bytes; or stream mode was selected and the response is a handle. Decode base64 before saving, or use the client’s IO stream reader and close the stream.
CDP command is unavailable The session is attached to the wrong target, or the Page domain was not enabled in a client that requires it. Check the attached page target and enable the Page domain as appropriate for the chosen binding.

8. Performance, reliability, and cost

CDP does not establish a universal rendering time or resource cost. Work depends on the page, its assets, browser setup, and the amount of content printed. For a reliable service, set navigation and readiness timeouts, close sessions and browser processes in cleanup paths, and handle navigation or print failures explicitly. Reuse browser infrastructure only when your application’s lifecycle and isolation requirements allow it; the protocol documentation does not define an operating model or performance guarantee.

Base64 is simple but places the encoded data in the command response, so applications handling large PDFs may prefer stream transfer if their client supports it. Account for the PDF bytes and any temporary buffers when choosing how to store or forward output. The dossier provides no benchmark or pricing data for running Chrome, so estimate costs from your own browser hosting and workload.

9. Or skip the browser setup

If you only need a PDF from a URL, ScreenshotNeo provides a website screenshot API and MCP server. Its PDF endpoint accepts a single GET request; see the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -d format=pdf -o page.pdf

Cookie and consent banners are accepted and removed before capture, along with known newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card.

10. FAQ

Do I need Puppeteer to use Page.printToPDF?

No. Puppeteer is one way to manage Chrome and its CDP session. You can call the protocol through another CDP client that supports the Page domain.

Does Page.printToPDF wait for every script and image?

No universal completion guarantee follows from navigation. Choose readiness checks that match the page and the output you need.

Does enabling tagged PDF guarantee accessibility?

No. The protocol exposes a tagged-PDF option, but that alone does not promise a conformance level.

When should I use stream transfer?

Use it when your client supports protocol IO streams and you prefer reading the PDF through a stream handle. For simpler workflows, base64 data is easier to decode and save.