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.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →| 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.
#1 Best Overall
Install and run a basic converter
- Install Node.js and create a project:
mkdir url-pdf && cd url-pdf && npm init -y. - Install Puppeteer:
npm install puppeteer. The package downloads a compatible browser unless your deployment is configured to use an existing executable. - Create
url-to-pdf.mjswith 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.
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
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.
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.
Rank #3
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.
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.
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.
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.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →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.
Best Value
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:
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
- Choose print or screen media and record the decision.
- Define paper size, orientation, margins, background-color behavior, and page-break rules.
- Identify a deterministic readiness signal for dynamic content.
- Test authenticated, asset-heavy, long, and failure pages.
- Apply SSRF protection, credential isolation, timeouts, memory limits, and concurrency limits.
- Inspect generated PDFs for fonts, links, pagination, colors, and file validity.
- 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.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsHow 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.
Quick Recap
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.

