October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Axios

How to Convert HTML to PDF in Node.js with Axios

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

Axios fetches the HTML; Puppeteer renders it and creates the PDF. Axios is an HTTP client, not an HTML-to-PDF engine. A dependable Node.js pipeline is to request HTML with Axios, load the returned markup into a Puppeteer page with page.setContent(), and call page.pdf(). If the source is already a web page, skip Axios and let Puppeteer navigate directly to the URL.

The roles of Axios and Puppeteer

Axios handles the network request. Its response includes data (the HTML body), status (for example, 200 or 404), and headers. It does not execute JavaScript, apply CSS in a browser, wait for fonts, or lay out a document.

Puppeteer controls Chromium. page.setContent(html) places an HTML string in a page, while page.pdf() returns PDF bytes as a Promise<Uint8Array> or writes them to a path. This separation lets you fetch a template, generate HTML in your application, or render a remote page with browser behavior.

Workflow Use it when Important trade-off
Axios + setContent Your application fetches or generates the markup first. Relative images, stylesheets, and fonts need a usable base URL or absolute URLs.
Puppeteer navigation + pdf The browser-rendered state of an existing URL is the document you need. Navigation timing and asynchronous page behavior affect the result.
PDFKit You can construct every PDF element through a document API. Its documented API creates and streams PDFs; it is not a browser HTML/CSS renderer.

Install the dependencies

Initialize a project and install compatible versions of Axios and Puppeteer. Puppeteer normally downloads a compatible browser during installation; some containers and restricted build systems require you to install or configure a browser separately.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npm init -y
npm install axios puppeteer

If you use ECMAScript modules, add "type": "module" to package.json, or use the CommonJS variant shown later. Verify the package versions against your Node.js runtime and deployment image rather than assuming that a command written today remains compatible indefinitely.

Convert a remote HTML URL with Axios

The following function downloads an HTML document, validates the HTTP status, renders it, and returns PDF bytes. The A4 paper size, print background, and cleanup structure are choices for this example; adjust them to your document and validate the output in your runtime.

import axios from 'axios';
import puppeteer from 'puppeteer';

export async function htmlUrlToPdf(url) {
  const response = await axios.get(url, {
    responseType: 'text',
    timeout: 30_000,
    headers: {
      'User-Agent': 'html-to-pdf-service/1.0'
    }
  });

  if (response.status < 200 || response.status >= 300) {
    throw new Error(`HTML request failed: ${response.status}`);
  }

  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.setContent(response.data, { waitUntil: 'networkidle0' });
    return await page.pdf({
      format: 'A4',
      printBackground: true,
      margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' }
    });
  } finally {
    await browser.close();
  }
}

const pdf = await htmlUrlToPdf('https://example.com/invoice.html');
// Send pdf as an HTTP response, store it, or return it from a job.

setContent receives markup, not a URL. A remote document that contains relative references such as /styles.css may lose those resources when inserted as a standalone string. Prefer absolute asset URLs, add a suitable <base href="https://example.com/"> element before the rest of the markup, or use direct browser navigation when the original page context matters.

Return the PDF from an Express route

import express from 'express';
import { htmlUrlToPdf } from './html-url-to-pdf.js';

const app = express();
app.get('/pdf', async (req, res, next) => {
  try {
    const pdf = await htmlUrlToPdf('https://example.com/invoice.html');
    res.type('application/pdf').set('Content-Disposition', 'inline; filename="invoice.pdf"').send(Buffer.from(pdf));
  } catch (error) {
    next(error);
  }
});
app.listen(3000);

Convert an HTML string

When a template is already in memory, Axios is unnecessary. Pass the string directly to Puppeteer. This is also the pattern to use after rendering a server-side template.

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';

export async function htmlStringToPdf(html) {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.setContent(html, { waitUntil: 'networkidle0' });
    return await page.pdf({ format: 'A4', printBackground: true });
  } finally {
    await browser.close();
  }
}

const html = `<!doctype html>
<html><head><style>body{font-family:Arial} h1{color:#163b70}</style></head>
<body><h1>Invoice 1042</h1><p>Amount due: $125</p></body></html>`;
const pdfBytes = await htmlStringToPdf(html);

Never concatenate untrusted values into HTML without escaping or sanitizing them. If users can supply markup, isolate the renderer and apply the network restrictions described below.

Convert the browser-rendered URL directly

Use navigation when JavaScript, cookies, client-side routing, or document-relative assets are part of the desired output. Puppeteer's guide commonly demonstrates a networkidle2 wait, but no single wait condition proves that every application-specific request has finished.

import puppeteer from 'puppeteer';

export async function pageUrlToPdf(url) {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 60_000 });
    await page.emulateMediaType('print');
    return await page.pdf({ format: 'A4', printBackground: true });
  } finally {
    await browser.close();
  }
}

For an application with a known readiness signal, wait for that signal instead of guessing:

await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-pdf-ready]', { timeout: 30_000 });
const pdf = await page.pdf({ format: 'A4', printBackground: true });

Control media, color, and page layout

Print versus screen CSS

PDF generation uses print media by default. To reproduce screen styles, call await page.emulateMediaType('screen') before page.pdf(). Keep print-specific rules in @media print when the PDF should differ from the interactive page.

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

Printing can alter colors. Add this CSS when exact color reproduction is important, then inspect the actual PDF:

* {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

Common PDF options

  • format: a standard paper size such as A4 or Letter.
  • landscape: use true for wide tables or reports.
  • margin: set top, right, bottom, and left values with CSS units.
  • printBackground: include CSS backgrounds and background images.
  • path: write the PDF directly to a file instead of using returned bytes.
  • displayHeaderFooter, headerTemplate, and footerTemplate: add page decorations where your Puppeteer version supports them.
  • pageRanges: export selected pages after the document has been laid out.

Use CSS controls such as break-before, break-after, and break-inside: avoid for headings, cards, and table rows. Test long tables, margins, orientation, headers, footers, and page breaks with representative data; these are layout outcomes, not guarantees.

Fonts, images, and asynchronous resources

Puppeteer's guide says PDF generation waits for fonts by default. External stylesheets, images, scripts, and web fonts still depend on reachability and timing. For a generated document, make assets absolute or provide a base URL. For a navigated page, wait for a document-specific readiness marker or for images to complete when necessary.

await page.goto(url, { waitUntil: 'networkidle2' });
await page.evaluate(() => Promise.all(
  [...document.images].map(img => img.complete
    ? Promise.resolve()
    : new Promise(resolve => { img.addEventListener('load', resolve); img.addEventListener('error', resolve); }))
));
await page.pdf({ format: 'A4', printBackground: true });

This image wait prevents one broken image from hanging forever, but it does not make an inaccessible resource appear. Confirm that the renderer can resolve DNS, certificates, authentication, and cross-origin resources in its deployment environment.

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.

Reliability, performance, and security

Browser lifecycle

Always close the browser in a finally block. For a high-volume service, reusing a browser process can avoid repeated startup work, but design page isolation, concurrency limits, crash recovery, and cleanup for your workload. There is no universal speed or memory number: page complexity, fonts, images, scripts, and the hosting environment dominate.

Untrusted HTML and URLs

A renderer is a network-capable component. If callers control HTML or URLs:

  • Allow-list destinations where possible and block private-network, loopback, and metadata-service addresses.
  • Do not forward application credentials, cookies, or authorization headers unless required.
  • Sanitize user HTML and avoid executing untrusted scripts when a static template is sufficient.
  • Apply request and navigation timeouts, memory limits, and concurrency limits.
  • Log status, URL, duration, and failure reason without recording secrets.

Puppeteer request interception can enforce policy, but every intercepted request must be continued, answered, aborted, or served from cache. A handler that forgets to resolve a request can stall the page.

Axios request handling

Set a finite timeout, check the status, and use responseType: 'text' when you expect HTML. Follow redirects deliberately, cap response size where appropriate, and treat non-HTML responses as errors. An HTTP 200 response can still contain an error page, so validate expected markers before rendering.

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

Common failures and fixes

Symptom Likely cause Fix
Axios returns 401, 403, or 404 Authentication, access control, or an incorrect URL. Check the URL and required headers; do not bypass access controls. Log status and response headers safely.
PDF is blank Markup was empty, a client app had not rendered, or a required resource failed. Inspect response.data, use navigation for client-rendered pages, wait for a readiness selector, and check browser console/network errors.
Styles or images are missing Relative URLs have no document base, or resources are unreachable. Use absolute URLs or a base element; verify DNS, certificates, and authentication from the renderer.
Colors differ from the page Print media rules and color adjustment changed the result. Choose emulateMediaType('screen') when appropriate and use print-color-adjust: exact.
networkidle never arrives Analytics, sockets, polling, or another long-lived request keeps the network busy. Use a shorter navigation condition and wait for a specific selector or application event.
Browser fails to launch in production Missing Chromium libraries, sandbox restrictions, or an incompatible image. Use a compatible Puppeteer/browser installation, follow the container's sandbox requirements, and test the exact deployment image.
Process hangs during interception An intercepted request was not resolved. Ensure every handler calls continue(), respond(), or abort().
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 your requirement is simply a clean screenshot or PDF of a URL, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. 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.

One GET request returns a PNG, JPEG, WebP, or PDF:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for output and options. Its 63 options include full-page lazy-image capture, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page settings, custom CSS and JavaScript, clicks, hide selectors, selector/delay/network-idle waits, request blocking, headers, cookies, user agent and authorization, timezone and geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous signed webhooks, bulk capture for 100 URLs per call, usage data, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work.

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

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, with yearly billing providing two months free. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Sign up for the free 1,000-shot plan.

When PDFKit is a better fit

Choose PDFKit when the output is a programmatically drawn report: text blocks, lines, images, and tables whose positions you control. It exposes a PDF document API and Node stream output. Choose Puppeteer when fidelity to HTML and CSS, browser fonts, responsive layout, or client-side rendering matters. PDFKit is not a drop-in replacement for browser rendering.

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

FAQ

Can Axios convert HTML to PDF by itself?

No. Axios retrieves bytes over HTTP. A renderer such as Puppeteer must interpret HTML and produce the PDF.

Should I use setContent or goto?

Use setContent for HTML you fetched or generated. Use goto when the live browser page, its scripts, cookies, and relative assets are the intended source.

Can I return a PDF without creating a temporary file?

Yes. page.pdf() returns PDF bytes, which you can send as an HTTP response or store in object storage.

Frequently Asked Questions

Does Puppeteer wait for web fonts before creating the PDF?

Puppeteer's guide states that PDF generation waits for fonts by default, but external CSS, images, and other resources still require validation in your loading flow.

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

Why does a page look different in the PDF than in Chrome?

PDF output uses print media by default. Select screen media when needed, account for print CSS, and inspect color-adjustment and page-break rules.

Is a browser required for every HTML-to-PDF workflow?

A browser is needed for browser-faithful HTML/CSS rendering. A library such as PDFKit can create PDFs directly when you are willing to construct the document through its API.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.