How to Convert HTML to PDF: Server-Side, Browser-Side, and When to Pick Which

By PDFKits Team — Published February 19, 2026

TL;DR. Converting HTML to PDF means deciding where rendering happens. Browser print-to-PDF (Ctrl+P → Save as PDF) is fine for one-off saves. Headless Chrome (via Puppeteer or Playwright) is the right approach for production pipelines that generate invoices, reports, or shipping labels — it renders modern CSS, web fonts, and dynamic content faithfully because it IS Chrome. Server-side libraries (WeasyPrint, wkhtmltopdf, PDFKit-JS) trade CSS support for predictability. For ad-hoc conversion of a web page or saved HTML file, browser-only tools like PDFKits handle the file without uploading anything. The right pick is the one whose CSS support matches your HTML's complexity.

Three Conversion Paths and What Each Costs

HTML-to-PDF is one of those problems that looks trivial and turns out to depend on what you're trying to do. The first decision is rendering engine: full-featured Chromium (handles modern CSS, web fonts, flexbox, grid, JavaScript-rendered content) or a lighter renderer (faster, more predictable, but with quirks around CSS support). The second is where it runs: in a browser (interactive, single document at a time), in a headless process (batch-capable, scriptable, repeatable), or in a fully isolated library (no Chromium dependency, deployable on minimal servers).

Each path has a typical user profile. Marketing and ops teams generating receipts, reports, and shipping labels lean headless-Chrome because the input HTML is complex and the output needs to look pixel-perfect. Developers shipping documents from a constrained backend (Lambda, container) lean WeasyPrint or wkhtmltopdf because the binary footprint matters. Knowledge workers saving a single web page lean browser print-to-PDF because it requires zero setup.

The Five Common Conversion Workflows

The accounts team generating customer invoices

Priya runs the billing operations for a 50-person SaaS company. Customer invoices are generated from an HTML template populated with order data, then rendered to PDF for emailing. The team uses Puppeteer in a Node.js service: each invoice request returns an HTML page from the billing API, headless Chrome renders it, and the PDF goes to the customer's email and the accounting archive. The setup handles 2,000+ invoices per month with no human in the loop.

The marketing team archiving landing-page changes

Daniel manages landing pages for a B2C startup. Each A/B test variant needs a PDF snapshot for the experiment archive — the visual baseline that the test was run against. Browser print-to-PDF works for the occasional one-off, but for the team's standard process they use a small Playwright script that takes a URL, waits for the page to fully load (including web fonts and lazy-loaded images), and saves the result. Snapshots go into the experiment results doc.

The shipping operations generating labels

Carlos works at a fulfillment center that produces 800–1,200 shipping labels daily. The carrier API returns label HTML; the system converts to PDF for the warehouse's thermal printers. wkhtmltopdf is the tool of choice — fast, predictable for the simple, repetitive label HTML, and runs inside the existing Linux infrastructure without a Chromium dependency.

The journalist saving sources

Amara archives source material as PDFs — articles, government press releases, social media posts she wants to cite. Browser print-to-PDF, sometimes combined with reader-mode in Firefox or Chrome, is the right tool: one click, looks fine, no upload, no third-party service. For pages with paywalls or complex rendering, she uses PDFKits' tools to clean up the resulting PDF afterward.

The legal team building exhibits from web pages

Marcus assembles trial exhibits including snapshots of websites at specific points in time. The print-to-PDF output is filed as Exhibit B-12, with the URL and timestamp visible in the page footer (browser settings). For more rigorous archival (e.g., to file as evidence under hearsay exceptions for business records), the team uses a screenshot-plus-metadata workflow rather than a "save as PDF" — but for routine exhibits, browser print is enough.

How to Convert HTML to PDF in PDFKits

Open the Create PDF tool. Paste a URL or upload an HTML file. Choose page size (A4, US Letter, fit-to-content), orientation, and margins. Optionally toggle header/footer with the URL and date. Click Create. PDFKits renders the HTML in your browser, produces the PDF, and offers it for download. The file is built locally — useful for HTML files that contain client information you don't want to upload to a third-party converter.

For HTML that depends on JavaScript-rendered content, the tool fetches the page after JS execution where possible. For pages behind authentication or paywalls, save the rendered HTML locally first (right-click → Save Page As) and upload the saved HTML — the conversion will use whatever is in the saved file.

Each Method's Strengths

Browser print-to-PDF (Ctrl+P)

Best for: one-off saves, journalists, knowledge workers, casual archiving.
Strengths: zero setup, identical to what you see on screen, supports all CSS and fonts that the browser supports.
Weaknesses: cannot automate, page breaks can be awkward, header/footer customization is limited to what the browser exposes.

Headless Chrome (Puppeteer / Playwright)

Best for: production pipelines, complex HTML, modern CSS, dynamic content.
Strengths: pixel-perfect parity with Chrome, full CSS and JavaScript support, scriptable batches.
Weaknesses: heavy binary (Chromium is ~150 MB), slower per-document than lighter tools, requires Chrome execution environment.

wkhtmltopdf

Best for: high-volume simple HTML (labels, receipts, basic reports).
Strengths: fast, lightweight, runs in any environment with Qt.
Weaknesses: WebKit-based renderer is years behind Chrome — modern CSS features (CSS Grid in particular) often fail. Project is in maintenance mode; for new builds, prefer headless Chrome.

WeasyPrint

Best for: server-side document generation in Python, when CSS Paged Media is needed.
Strengths: pure Python, no Chromium, excellent support for CSS Paged Media (page numbers, headers, footers, named pages).
Weaknesses: limited JavaScript support, slower than wkhtmltopdf for simple HTML.

PDFKit-JS / jsPDF

Best for: programmatic PDF generation where you control the layout in code.
Strengths: client-side, no HTML rendering — you draw the PDF directly. Small footprint.
Weaknesses: not actually an HTML-to-PDF tool — you write code to position text and images, which is not what you want if you have existing HTML.

PDFKits vs. the Conversion Landscape

FeaturePDFKitsPuppeteer (DIY)SmallpdfAdobe Acrobat Online
CostFreeFree (run yourself)$108/year$29.99/month
Files stay on your deviceYesYes (your server)No — cloudNo — cloud
Modern CSS (Grid, custom fonts)Yes (browser-based)Yes (Chrome)YesYes
Batch / scriptableNo (UI only)YesLimitedLimited
Per-page header/footer controlBasicFull (Puppeteer API)LimitedYes
Save authenticated pagesYes (via local file)Yes (script the auth)LimitedLimited
No installationYesNo (Node + Chromium)YesYes

For an individual saving a single page, PDFKits or browser print are both fine and identical in privacy. For a production pipeline shipping thousands of documents per day, Puppeteer or a similar headless setup is the only viable option — neither online tool nor manual print scales.

The CSS Pitfalls That Catch Everyone

Background colors and images. Most browser print dialogs default to "no background graphics" to save ink. If your HTML uses colored backgrounds for headers or callouts, enable "Background graphics" in the print options or pass printBackground: true to Puppeteer.

Web fonts that don't load before render. Headless Chrome can start rendering before web fonts finish loading. The fix is page.evaluateHandle('document.fonts.ready') in Puppeteer, or in browser print, wait for the page to fully load (visible delay of 1–2 seconds) before invoking Save as PDF.

Page break control. Use page-break-before: always, page-break-after: avoid, and page-break-inside: avoid (or the newer break-* properties) to control where pagination occurs. Without these, a chart or table may be split mid-row.

Fixed vs absolute positioning. position: fixed elements behave inconsistently across renderers — sometimes the element repeats on every page, sometimes it appears only on page one. Test with multi-page content before deploying.

Media queries. Use @media print to define print-specific styles — hide navigation, expand collapsed content, switch to print-friendly fonts. Many web pages look much better in PDF after adding a 20-line print stylesheet.

Frequently Asked Questions

Why does my PDF look different from the web page?

Three usual suspects: print-only CSS (the page has @media print rules), missing background colors (print dialog stripped them), or web fonts that hadn't loaded when the snapshot was taken. Check the page's stylesheet for @media print rules, and verify fonts are loaded before converting.

Can I convert a logged-in page (paywall, dashboard) to PDF?

Yes, but you have to be logged in at the time of conversion. Browser print works directly. For programmatic conversion, the script needs to handle authentication — log in to capture session cookies, then pass them to the headless browser before navigating to the page.

What about pages with infinite scroll?

Browser print typically captures only what's visible. For infinite-scroll pages, scroll to the bottom first (manually or via a script that triggers all scrolling) before invoking print. Some sites have a "view as article" or "single-page view" mode that bypasses the issue.

Is the output PDF accessible (screen-reader-friendly)?

It depends on the input HTML. If the HTML has proper semantic tags (headings, alt text on images, ARIA labels), the resulting PDF usually inherits the structure as tags. Untagged HTML produces untagged PDF. For full PDF/UA compliance, post-process with Adobe Acrobat Pro.

How do I keep page numbers and headers consistent?

In Puppeteer: pass displayHeaderFooter: true with templated HTML for header and footer. In browser print: the dialog has options for header and footer text. For full control, use CSS Paged Media @page rules with WeasyPrint.

Will my PDF have the same fonts as the web page?

Yes, if the fonts are loaded at conversion time. Web fonts referenced via @font-face in the page's CSS are embedded into the PDF by headless Chrome and by browser print. Local fonts available on the user's system are also embedded.

How big will the PDF be?

For a typical content page with text and a few images, expect 200 KB to 2 MB. Pages with many high-resolution images can exceed 10 MB. Run Optimize PDF on the result to bring file size down to web-shareable levels.

Can I batch-convert 500 URLs to PDF?

Not from a UI tool — that's where Puppeteer scripts shine. A short Node.js script can iterate URLs, convert each, and save with a sensible filename. Plan on 1–3 seconds per page on average hardware.

What about converting just part of a page (e.g., main article without sidebar)?

Use the page's reader mode (Firefox, Edge, Chrome with extensions) to strip chrome before saving. Or in a script, scrape the article container only and convert that fragment. Both approaches avoid carrying ads and navigation into the PDF.

Will SVG render correctly in the PDF?

SVG renders cleanly through Chrome-based converters (Puppeteer, browser print). wkhtmltopdf and some older tools rasterize SVG, producing blurry output. If SVG fidelity matters, use a Chrome-based converter.

Related PDFKits Tools

Create PDF (HTML to PDF) — Convert URLs and HTML files in the browser. Merge PDF — Combine converted pages with other PDFs. Optimize PDF — Shrink large converted PDFs for sharing. Edit PDF — Post-conversion edits to the PDF output. Clean Metadata — Strip URL and creator info before sharing. Page Numbers — Add consistent page numbers to multi-page exports.