Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

There is no universal Chrome Headless PDF switch that fixes every rendering defect. The reliable approach is to reproduce the problem with the same Chrome/Puppeteer versions, HTML, CSS, fonts, operating system and PDF options used in production, then check settings in a fixed order: print media, page geometry, colors, content readiness, browser headers and runtime differences.

Start with a minimal, repeatable reproduction

Save the exact production HTML and assets, and record:

  • Chrome or Chromium build number.
  • Puppeteer version and Node.js version.
  • Operating system or container image.
  • Installed fonts and font files used by the page.
  • Viewport, device scale factor and every page.pdf() option.
  • The URL, cookies, authentication state and JavaScript data needed to render the page.

Compare the generated PDF with a screenshot of the same document in desktop Chrome using identical content. Change one variable at a time and keep the smallest reproducer; otherwise a CSS change can hide the real cause.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Baseline Puppeteer script

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({headless: true});
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 900, deviceScaleFactor: 1 });
  await page.goto('https://example.com', { waitUntil: 'networkidle0' });
  await page.pdf({
    path: 'output.pdf',
    format: 'A4',
    printBackground: true,
    displayHeaderFooter: false,
    preferCSSPageSize: true,
    waitForFonts: true
  });
  await browser.close();
})();

Replace the URL and options with your production values. Puppeteer’s page.pdf() generates a PDF with the print CSS media type by default, so a browser window that looks correct on screen is not necessarily the reference output.

1. Check print media before changing layout

Inspect every @media print rule, inherited property and @page rule. Print styles often hide navigation, change colors, alter display modes or set different widths. A rule such as display:none or a print-only fixed width can explain an apparently missing or clipped element.

When the PDF should match the screen

Explicitly emulate screen media before calling page.pdf():

await page.emulateMediaType('screen');
await page.pdf({ path: 'screen-like.pdf', printBackground: true });

This does not make a PDF identical to a screenshot: pagination, paper dimensions and print-specific browser behavior still apply. If the document is intended for printing, keep the default print media and fix the print stylesheet instead.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Make page dimensions and scaling agree

Chrome can receive page geometry from CSS and from Puppeteer. Compare them rather than compensating with arbitrary widths or transforms.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
Setting What it controls Important behavior
@page { size: ... } CSS page size and orientation Used when CSS page size is given priority.
format Named paper size such as A4 or Letter Provides Puppeteer’s paper size when width/height are not supplied.
width, height Explicit paper dimensions Use consistent units and check orientation.
preferCSSPageSize Precedence between CSS and Puppeteer dimensions Defaults to false; set true when CSS @page must win.
margin Printable inset Large margins reduce the content box and can trigger unexpected wrapping.
scale Overall PDF scaling Review it together with dimensions and margins.

For a CSS-controlled document:

@page {
  size: A4 portrait;
  margin: 14mm;
}

html, body {
  margin: 0;
}

@media print {
  .screen-only { display: none !important; }
}
await page.pdf({
  path: 'report.pdf',
  preferCSSPageSize: true,
  printBackground: true,
  margin: { top: '0', right: '0', bottom: '0', left: '0' }
});

Do not define a conflicting format while diagnosing a CSS-size problem. Conversely, if your service contract is “always Letter,” set that format explicitly and design the CSS for its resulting content width.

3. Restore backgrounds and intended colors

Puppeteer’s printBackground option defaults to false. Enable it when panels, gradients, images or colored table cells are part of the design.

await page.pdf({
  path: 'colored.pdf',
  printBackground: true
});

Chrome also modifies colors for printing by default. Request exact CSS colors where appropriate:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
@media print {
  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

Exact color adjustment can increase ink or produce a less printer-friendly document, so apply it deliberately rather than globally when accessibility or paper economy matters.

4. Wait for fonts and application content

Puppeteer’s waitForFonts option defaults to true and waits for document.fonts.ready. That only confirms the browser’s font-loading promise; it does not prove that your application has fetched data, finished hydration or rendered charts.

Verify fonts explicitly

await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.evaluate(() => document.fonts.ready);
await page.waitForSelector('[data-pdf-ready]', { visible: true });
await page.pdf({ path: 'report.pdf', waitForFonts: true, printBackground: true });

Make data-pdf-ready appear only after the page has its final data and layout. Also check the browser logs and network responses for blocked font files, incorrect MIME types, CORS failures and authentication-dependent URLs. A missing font changes glyph widths and can cascade into different line breaks and page counts.

Control time-dependent pages

Animations, delayed API calls and clocks can produce different output on every run. Disable animations in a print stylesheet or inject a deterministic class, then wait for a selector or application-specific state. When using the Chrome command line, --timeout bounds capture timing and --virtual-time-budget gives time-dependent code a controlled virtual interval; neither value guarantees that a particular application is ready.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Remove unexpected Chrome headers and footers

Headers containing the date and time, and footers containing the URL or page number, are browser print furniture—not page content. In Puppeteer, use displayHeaderFooter: false to suppress them, or set it to true only when supplying intentional templates.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
await page.pdf({
  path: 'clean.pdf',
  displayHeaderFooter: false
});

For the Chrome CLI, the current flag is --no-pdf-header-footer. Older Chrome versions used --print-to-pdf-no-header; if the current spelling is rejected, check the installed build’s CLI documentation rather than assuming the PDF engine is broken.

6. Compare the actual runtime

Reproduce with the same Chrome build, Puppeteer release, OS or container, font packages and launch flags. Desktop Chrome and a Linux container can differ in font fallback, sandboxing, GPU availability and default resources even when the HTML is identical.

A historical Puppeteer issue (#2278, opened March 28, 2018) described a page-size discrepancy involving Puppeteer 1.2.0, macOS 10.13.3 and desktop Chrome 65. It is evidence that environment comparisons matter, not proof of a current universal defect. Do not “fix” a present release solely by copying a workaround for that old report.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Symptom-to-fix troubleshooting

Symptom Likely cause First corrective action
Screen layout differs from PDF Print media rules are active Inspect @media print; use emulateMediaType('screen') only if screen styling is the requirement.
Wrong paper size or unexpected whitespace Conflicting CSS and Puppeteer dimensions, margins or scale Choose one authority; set preferCSSPageSize deliberately and verify orientation.
Colored sections are white printBackground is false Set it to true and review print color adjustment.
Text wraps differently or uses a fallback font Font failed to load or readiness was too early Inspect requests, await document.fonts.ready and verify installed fonts.
Charts or data are missing JavaScript had not reached its final state Wait for an application readiness selector/state, not only networkidle0.
Date, URL or page number appears Print header/footer enabled Disable displayHeaderFooter or use the version-appropriate CLI flag.
Runs differ between machines Chrome, fonts, OS or container mismatch Pin and record the complete runtime; compare generated PDFs from the same image.
Capture times out Slow assets, blocked requests or an unbounded readiness condition Inspect failed requests, set an explicit timeout policy and ensure the ready selector can occur.

Performance, reliability and cost considerations

  • Reuse a browser process when safe, but create an isolated page and context per job so cookies and styles do not leak.
  • Wait for the smallest reliable readiness condition; waiting indefinitely for global network idle can stall pages with analytics or long polling.
  • Block nonessential ads and trackers only when doing so cannot change the document’s layout or required data.
  • Store the Chrome build and PDF options alongside the artifact so a later visual difference is diagnosable.
  • Use deterministic fonts, locale, timezone and data fixtures for regression tests. Compare page count, dimensions and rasterized pages, not only file size.
  • Set job timeouts and clean up pages and browsers in failure paths. A timeout should produce a useful error and leave no orphaned Chrome processes.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need a hosted capture path instead of operating Chrome yourself, ScreenshotNeo provides a website screenshot API and MCP server, and its PDF endpoint can capture a URL in one request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.

One-call examples

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the complete option names and PDF controls in the ScreenshotNeo documentation. Every feature is on every plan: 1,000 shots per month are free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Should I use networkidle0 for every PDF job?

No. Pages with analytics, polling or streaming connections may never become idle. A page-specific readiness selector or application state is usually more reliable.

Why does changing the viewport not fix the PDF width?

Viewport size affects layout before printing, while paper dimensions, margins and scaling determine the PDF page. Check those settings separately.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can a PDF be pixel-identical to desktop Chrome?

Only when the browser build, fonts, OS, input and print settings match closely; PDF pagination and print media still make screen pixels an imperfect target.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.