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.

Short answer: load the URL or HTML in a browser rendering engine, wait for the page’s real content, choose print or screen CSS deliberately, set paper dimensions and margins, and export with the engine’s PDF method. Puppeteer and Playwright do this locally; hosted services do it without your team maintaining browser workers. The examples below show both approaches, including authentication, dynamic pages, print styling, troubleshooting, and production safeguards.

What actually happens when HTML becomes a PDF

A PDF is not a copy of the browser’s pixels. A rendering engine parses HTML, applies CSS, runs JavaScript, loads fonts and images, lays content onto pages, and then writes a PDF. Puppeteer and Playwright use Chromium’s print pipeline and generate with print CSS by default. Puppeteer describes its method plainly as generating “a PDF of the page with the print CSS media type.”

That distinction explains why a PDF can differ from what you see on screen: responsive breakpoints, print-only rules, page breaks, omitted backgrounds, and late-running application code all affect the result. Decide first whether you want a print-oriented document or a screen-like rendering.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Decision Use this when
Print CSS Invoices, reports, articles, and documents intended for paper or conventional PDF reading.
Screen CSS You need the PDF to resemble the interactive desktop layout, including screen-specific colors and spacing.
Local browser You need browser-level control, private-network access, or a fixed browser version and can operate workers.
Hosted API You prefer an HTTP call and managed browsers, retries, concurrency, storage, or asynchronous jobs.

Convert a URL to PDF with Puppeteer

Puppeteer is a Node.js browser-automation library. Its documented sequence is launch a browser, create a page, navigate, call page.pdf(), and close the browser. The API waits for fonts by default, but you still need a readiness condition for your application’s data.

Install and run a basic converter

  1. Install Node.js and create a project: mkdir url-pdf && cd url-pdf && npm init -y.
  2. Install Puppeteer: npm install puppeteer. The package downloads a compatible browser unless your deployment is configured to use an existing executable.
  3. Create url-to-pdf.mjs with this code:
import puppeteer from 'puppeteer';

const target = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch({
  headless: true,
  args: ['--no-sandbox', '--disable-setuid-sandbox']
});

try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
  await page.goto(target, { waitUntil: 'networkidle2', timeout: 90_000 });
  await page.pdf({
    path: 'output.pdf',
    format: 'A4',
    printBackground: true,
    margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' },
    displayHeaderFooter: false
  });
} finally {
  await browser.close();
}

Run it with node url-to-pdf.mjs https://example.com. The networkidle2 condition is only a useful starting point: analytics, WebSockets, polling, or a single-page app can keep requests active without meaning that the visible report is ready.

Wait for the content your page needs

For a known application, wait for a selector that appears only after rendering:

await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 90_000 });
await page.waitForSelector('[data-report-ready="true"]', { timeout: 30_000 });
await page.evaluate(() => document.fonts.ready);
await page.pdf({ path: 'output.pdf', format: 'A4', printBackground: true });

If there is no reliable selector, use a bounded delay as a last resort and document why it is sufficient. Waiting for fonts prevents fallback-font pagination, but it does not guarantee that images, charts, or client-side data have finished.

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.

Control print versus screen media

Puppeteer’s PDF method uses print media. To preserve screen rules, emulate screen media immediately before exporting:

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

Print output may alter colors. When exact backgrounds and brand colors matter, add -webkit-print-color-adjust: exact to the relevant CSS and still inspect the generated file.

Rank #2
Sale
Adobe Acrobat 6 PDF For Dummies
  • Used Book in Good Condition

Page size, margins, headers, and footers

Use a named format such as A4 or Letter, or provide explicit dimensions. Puppeteer’s PDF options include margins, background printing, landscape mode, page ranges, and HTML header/footer templates. Header and footer templates run in the PDF renderer and have restricted styling; test them with your actual content. Keep margins large enough that body content does not collide with them.

Generate a PDF from an HTML string

Use page.setContent() when the source is HTML you already own. A base URL is important: relative images, stylesheets, and fonts cannot load unless the document has a resolvable origin.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';

const html = `<!doctype html>
<html><head>
<meta charset="utf-8">
<style>
  @page { size: Letter; margin: 18mm; }
  body { font-family: Arial, sans-serif; color: #222; }
  h1 { break-after: avoid; }
  .avoid-split { break-inside: avoid; }
</style>
</head><body>
  <h1>Quarterly report</h1>
  <p class="avoid-split">Content supplied by the application.</p>
</body></html>`;

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.setContent(html, { waitUntil: 'networkidle0' });
  await page.evaluate(() => document.fonts.ready);
  await page.pdf({ path: 'html.pdf', format: 'Letter', printBackground: true });
} finally {
  await browser.close();
}

For external assets, prefer absolute HTTPS URLs or include the assets inline. If you must use relative paths, supply a meaningful base URL in the HTML and ensure the browser can reach it.

Playwright alternative

Playwright exposes a comparable page.pdf() API and returns a PDF buffer. It also defaults to print CSS; call page.emulateMedia({ media: 'screen' }) when screen media is required. Width and height accept units such as pixels, inches, centimeters, and millimeters.

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  await page.goto(process.argv[2] ?? 'https://example.com', {
    waitUntil: 'networkidle',
    timeout: 90_000
  });
  await page.waitForLoadState('domcontentloaded');
  await page.evaluate(() => document.fonts.ready);
  const pdf = await page.pdf({
    format: 'A4',
    printBackground: true,
    margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' }
  });
  await import('node:fs/promises').then(fs => fs.writeFile('playwright.pdf', pdf));
} finally {
  await browser.close();
}

The exact launch and browser-install commands vary by Playwright version and deployment image, so follow the current Playwright Page API for your installed release.

CSS that makes PDFs predictable

Declare the page and margins

@page {
  size: A4;
  margin: 16mm 14mm 18mm;
}

Keep related content together

.card, figure, table, .signature {
  break-inside: avoid;
}
h2, h3 {
  break-after: avoid;
}

Control color and links

html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
a { color: #111; text-decoration: underline; }

Test long tables, very tall images, SVG, web fonts, and content that changes after a user action. A rule that works on one template is not a cross-site guarantee.

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

Authentication, cookies, and protected pages

A browser script can establish a session before navigation, set cookies, or add request headers. Treat credentials as secrets and never place them in a public PDF URL or log them.

await page.setExtraHTTPHeaders({ Authorization: `Bearer ${process.env.API_TOKEN}` });
await page.setCookie({
  name: 'session',
  value: process.env.SESSION_COOKIE,
  domain: 'app.example.com',
  path: '/',
  secure: true,
  httpOnly: true
});
await page.goto('https://app.example.com/report', { waitUntil: 'domcontentloaded' });

For pages behind a login form, automate the login in an isolated browser context, wait for a post-login selector, then navigate to the report. Block downloads and third-party requests you do not need. Do not expose a converter endpoint that lets arbitrary callers fetch internal URLs; validate allowed hosts and prevent server-side request forgery.

Hosted HTML-to-PDF services

A managed service removes browser binaries, worker lifecycle, concurrency, retries, and often storage integration from your application. CloudConvert documents Chrome-based HTML-to-PDF conversion, URL or HTML-file inputs, custom authorization headers, a custom selector wait, synchronous or asynchronous jobs, and object-storage integrations. Its page displayed a starting price of $0.008 per file on September 29, 2026; pricing is volatile, so verify the current rate before budgeting.

DocRaptor accepts either document_url or document_content and exposes print/screen media settings. Its API reference describes Pipeline 10.1 as the default for users on the newest pipeline, mapping to Prince 15.1 and JavaScript engine 2; that versioned implementation detail can change. Test PDFs are watermarked and test mode has limitations.

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

PDFShift accepts raw HTML or URLs. Its pricing page advertised up to 50 credits per month on a free plan when checked; credit rules and plans can change.

Question Local browser Hosted API
Rendering control Pin browser version, flags, fonts, and network policy. Use the provider’s documented engine and options.
Authentication Direct access to your session, headers, and private network. Check support for headers, cookies, signed URLs, and private access.
Operations You manage workers, memory, timeouts, queues, and upgrades. Provider may supply sync/async jobs, retries, and storage hooks.
Privacy Content stays in your environment if your infrastructure is isolated. Review retention, hosted URL exposure, region, and terms before sending sensitive HTML.
Cost Pay for compute, browser memory, and engineering time. Pay per file, credit, size, or plan; current limits and prices vary.

Performance and reliability in production

  • Reuse a browser process but create a fresh page or context per job; never share cookies between tenants.
  • Set navigation, selector, and overall job timeouts. Kill pages that exceed the budget.
  • Limit concurrency according to available memory. Chromium jobs can consume substantially more memory than a simple HTTP request.
  • Cache deterministic documents using a content hash and template version. Do not cache personalized output under a public key.
  • Record URL, rendering options, browser or provider version, duration, byte size, and failure reason without recording secrets.
  • Retry transient navigation or provider errors with exponential backoff, but do not blindly retry deterministic JavaScript failures.
  • Validate the result as a PDF, check a nonzero size, and optionally render representative pages in a visual regression test.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Why the PDF looks different from the webpage

Print media changed the layout

Print styles may hide navigation, change widths, or remove backgrounds. Use screen-media emulation only when that is genuinely the desired output; otherwise fix the print stylesheet.

Content was captured too early

Wait for an application-specific selector, fonts, images, and chart rendering. Network-idle is not proof that client-rendered content is complete.

Fonts or assets failed

Check browser logs and network responses. Use absolute asset URLs, ensure the runtime can resolve DNS and certificates, and wait for document.fonts.ready.

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

Pagination is unexpected

Set an explicit paper size and margins, remove fixed screen heights, and use break-inside, break-before, and break-after where appropriate. Very large unbreakable elements must still move to another page or overflow.

Colors are muted

Print rendering can modify colors. Enable background printing and use -webkit-print-color-adjust: exact when exact color reproduction is required, then inspect on the target PDF viewer and printer.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server that can return a PDF from a URL with one GET request. It accepts cookie and consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

For a URL request, use the documented endpoint and options at ScreenshotNeo’s API documentation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same service supports PDF controls such as paper size, margins, landscape orientation, and page ranges, along with full-page capture, lazy-image loading, CSS-selector element capture, custom CSS and JavaScript, click and wait conditions, request blocking, custom headers and cookies, user-agent, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable caching TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Plans are Free (1,000 shots/month, no card), Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing provides two months free, and every feature is included on every plan. Sign up free for 1,000 screenshots a month with no card.

Operational checklist before shipping

  1. Choose print or screen media and record the decision.
  2. Define paper size, orientation, margins, background-color behavior, and page-break rules.
  3. Identify a deterministic readiness signal for dynamic content.
  4. Test authenticated, asset-heavy, long, and failure pages.
  5. Apply SSRF protection, credential isolation, timeouts, memory limits, and concurrency limits.
  6. Inspect generated PDFs for fonts, links, pagination, colors, and file validity.
  7. Measure job duration, failure classes, output size, and current provider costs.

Frequently Asked Questions

Can JavaScript-rendered pages be converted to PDF?

Yes. Puppeteer, Playwright, and Chrome-based hosted services execute page JavaScript; wait for an application-specific ready selector rather than assuming navigation completion means the data is visible.

Should I use print CSS or screen CSS?

Use print CSS for document-style output and emulate screen media only when matching the on-screen layout is the requirement.

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

How do I convert a private URL safely?

Keep credentials server-side, isolate browser contexts, validate destination hosts to prevent SSRF, and confirm that any hosted provider supports your required headers, cookies, network access, and retention policy.

Why is my PDF missing backgrounds or colors?

Enable background printing and, when exact colors matter, set the CSS print-color adjustment property; then verify the result in the PDF viewer you support.

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.