Why the PDF from headless Chromium differs from Print to PDF in the browser
Same engine, different output: viewport, print media, backgrounds, default margins, headless font rendering. A list of the differences and the option that fixes each.
Updated 31 August 2026
People test with Ctrl+P, ship with headless Chromium, and are surprised the two files differ. They use the same layout engine; they do not use the same defaults.
Print media
The print dialog applies `@media print` styles. Headless `page.pdf()` does too — but only if you emulate print media first. Renderers that skip `emulateMedia("print")` produce the screen layout. PDFGeny emulates print.
Backgrounds
The browser dialog has a checkbox "Background graphics", off by default. The API has `print_background`; on by default in PDFGeny, off by default in raw Puppeteer. A white page where you expected a coloured header is this setting.
Margins and page size
The dialog uses printer defaults. The API uses whatever you pass — `format` and `margin` — and otherwise Letter with roughly 0.4 inch margins. Set both explicitly.
Viewport width
Responsive CSS reacts to viewport width. In the print dialog the viewport is the page width; in headless mode it is whatever the browser was launched with (800px by default). Media queries can flip between the two. Set the viewport to the paper width, or write print styles that do not depend on breakpoints.
Fonts and hinting
Headless Linux renders text with different hinting than macOS or Windows; line breaks can move by a character. Embed web fonts and give the layout a few pixels of slack rather than pixel-perfect widths.
Timing
The dialog runs after you finished looking at the page. The API runs the moment the page reports load — unless you wait for fonts and network idle. Use `wait_for` when charts draw late.
Try it: convert HTML to PDF in the browser, or get a free API key — 50 documents a month.
More guides
Writing HTML templates for PDF: a style guide (Jinja, Django, Handlebars)
Templates that render well as PDFs follow a few rules: fixed page geometry, no external state, defensive filters, print CSS. Examples in Jinja and Django syntax.
Issuing 5,000 course certificates in one afternoon
A spreadsheet of names, one landscape template, batch requests of 100, and signed links per student. The full workflow including naming, verification codes and delivery.
Generating receipts at checkout without slowing the checkout
The receipt PDF should never sit between the customer and the order confirmation. A pattern: confirm first, render asynchronously, attach when ready.
URL-to-PDF and SSRF: how a PDF renderer becomes an attack surface
A renderer that fetches URLs can be pointed at your internal network. The attacks, the defences, and what to demand from a PDF API vendor.