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

Relative URLs in HTML resolve against the document’s base URL. When you pass an HTML string to Puppeteer’s page.setContent(), add a suitable <base href="…"> element in the document head, convert references to absolute URLs, or navigate to the page’s real URL with page.goto() before printing. The current setContent() API does not accept a baseURL option.

Why relative URLs break in a Puppeteer PDF

A reference such as images/chart.png or details.html is not a complete address. The browser resolves it against the document’s base URL. If the HTML you provide with page.setContent() assumes a website directory but the document does not have that base, a link may point to the wrong place and an image, stylesheet, or other asset may fail to load.

These are related but separate outcomes: an asset’s URL determines whether the browser can fetch it for printing, while an anchor’s URL determines its destination. Setting a base can give both kinds of reference the intended root, but only if they really share that root. If links and assets come from different locations, make their URLs explicit or resolve them separately.

Puppeteer’s current Page.setContent() signature accepts HTML and optional wait options; it has no baseURL parameter. The fix is therefore to establish the base in the HTML, encode full URLs in the markup, or load a real page at its intended address before calling page.pdf().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Epson EcoTank ET-2800 Wireless Color All-in-One Supertank Printer - Black
  • INNOVATIVE CARTRIDGE-FREE PRINTING — No more dealing with lots of tiny ink cartridges; With this wireless document and photo printer each ink bottle set is equivalent to about 90 individual cartridges²
  • LESS FREQUENT INK REPLACEMENT — Replacement ink bottles don't have to be changed nearly as often as ink cartridges¹; When you choose this combination printer, scanner and copier you can print up to 4,500 pages black/7,500 color³
  • COLOR PRINTING — Up to 2 years of ink in the box4 (and with every replacement ink set) for fewer out-of-ink frustrations
  • ZERO CARTRIDGE WASTE — By using an Epson EcoTank printer you can help reduce the amount of cartridge waste ending up in landfills
  • HOME PRINTER DESIGNED FOR RELIABILITY — The Epson EcoTank ET-2800 All-in-One Supertank Color Printer creates vivid, detailed prints and documents thanks to Micro Piezo Heat-Free Technology; Fire off 10 ISO pages per minute1 to easily finish large jobs

Fix 1: add a base URL to injected HTML

Put a <base href="…"> element in the document’s <head>, before markup that uses relative href or src values. Use the directory you want those references to resolve from:

<!doctype html>
<html>
<head>
  <base href="https://example.com/reports/">
  <meta charset="utf-8">
  <title>Monthly report</title>
</head>
<body>
  <a href="details.html">Details</a>
  <img src="images/chart.png" alt="Monthly chart">
</body>
</html>

With that base, the browser resolves details.html from the reports directory and images/chart.png from its images subdirectory. The slash at the end of /reports/ matters: it identifies the final segment as a directory. Without it, a relative reference can replace the final segment instead of being appended beneath it. Choose a base that matches the actual location of the resources and destinations; a base URL does not make an incorrect path correct.

Runnable example with page.setContent()

This example injects a page with the base element, waits for network activity to settle, and writes a PDF. Replace the example URLs with locations your Chromium process can reach:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    const html = `
      <!doctype html>
      <html>
      <head>
        <base href="https://example.com/reports/">
        <meta charset="utf-8">
        <title>Monthly report</title>
        <style>body { font: 16px Arial, sans-serif; }</style>
      </head>
      <body>
        <h1>Monthly report</h1>
        <img src="images/chart.png" alt="Monthly chart">
        <a href="details.html">Open details</a>
      </body>
      </html>`;

    await page.setContent(html, { waitUntil: 'networkidle0' });
    await page.pdf({ path: 'report.pdf', printBackground: true });
  } finally {
    await browser.close();
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

networkidle0 waits for the network to become idle under Puppeteer’s wait conditions; it is not proof that every asset rendered successfully. Pages with persistent network requests, delayed scripts, or lazy content may need a more specific readiness condition. If the exact image matters, check that it loaded before printing rather than assuming that a quiet network means a correct PDF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Epson EcoTank Photo ET-8550 Wireless Wide-Format All-in-One Tank Printer
  • CARTRIDGE-FREE PRINTING — Print lab-quality photos, graphics and creative projects; Get vibrant colors and sharp text with Epson's high-accuracy printhead and Claria ET Premium 6-color inks
  • INK BOTTLES — Save on photos1 and creative projects with affordable in-house printing; All-in-one printer allows you to print 4" x 6" photos for about 4 cents each vs. 40 cents with traditional ink cartridges1
  • LESS FREQUENT INK REPLACEMENT — Replacement ink bottles don't have to be changed nearly as often as ink cartridges¹; Printer, scanner and copier lets you print up to 6,200 color pages³
  • PRINT FOR LONGER — Up to 2 years of ink in the box² (and with every replacement ink set) for fewer out-of-ink frustrations with this wireless printer
  • ZERO CARTRIDGE WASTE — Epson EcoTank printer helps reduce the amount of cartridge waste ending up in landfills; Cartridge-free printer uses high-yield ink bottles; Each replacement ink bottle set is equivalent to about 100 individual ink cartridges⁴

Check the base path before printing

  • Use the complete intended origin and directory in the base value, including the correct scheme, hostname, and path.
  • Check whether references are directory-relative, such as images/chart.png, or root-relative, such as /images/chart.png. A root-relative path starts at the origin rather than under /reports/.
  • For a link or asset that belongs on another host or path, use its full URL or resolve it against its own base.
  • Ensure the page is allowed to request the asset and that the URL is reachable from the machine running Chromium.

Fix 2: convert relative references to absolute URLs

If you generate or transform the markup, resolve each relative reference against the intended source URL before giving the HTML to Puppeteer. This avoids relying on the eventual document base and makes the destination visible in the markup. In Node.js, the standard URL constructor performs the resolution:

const base = 'https://example.com/reports/';
const absoluteHref = new URL('details.html', base).href;
const absoluteSrc = new URL('images/chart.png', base).href;

console.log(absoluteHref); // https://example.com/reports/details.html
console.log(absoluteSrc);  // https://example.com/reports/images/chart.png

Use the actual source URL as the base, not automatically the site homepage. For example, resolving ../assets/logo.png against a report directory produces a different result from resolving it against the origin root. The result also depends on whether the base ends in a slash, so preserve the intended directory structure.

This approach is useful when you control a markup-generation step or when different classes of reference have different roots. It also makes debugging simpler: inspect the final href and src values, then verify those full addresses independently. It does not guarantee the resource is accessible; authentication, redirects, network policy, or a missing file can still prevent loading.

Fix 3: navigate to the page’s real URL

If the HTML is already served at the address it should use as its base, navigate to that address instead of injecting a string. Puppeteer’s navigation-then-print pattern is the simplest fit for an existing page:

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.
Rank #3
HP Smart Tank 5000 Ink Tank Printer | 2 Years of Ink Included | All-in-One
  • SET IT UP ONCE AND PRINT WITH CONFIDENCE. No complicated maintenance. Just easy, reliable printing you can count on.
  • INK FOR YEARS. NOT MONTHS. Up to 2 years of ink included. Get thousands of pages of cartridge-free printing. More pages, less hassle
  • KEEPS PRINTING WELL AFTER COMPETITORS HAVE QUIT. No complex maintenance. Sharper text, richer colors.[2] Only with HP Smart Tank
  • PREMIUM SUPPORT - Strong technical expertise to solve issues faster
  • THE LAST PRINTER YOU'LL EVER NEED. Enjoy years of refillable, cartridge-free printing.
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com/reports/monthly', {
      waitUntil: 'networkidle2',
    });
    await page.pdf({ path: 'report.pdf', printBackground: true });
  } finally {
    await browser.close();
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Navigation fits a document that exists at a real URL and should behave as it does when opened in a browser. A base element is usually the direct choice for generated HTML passed to setContent(). Absolute URLs are a good choice when each reference needs an explicitly controlled destination. Avoid navigating to an unrelated page just to establish a base: it captures that page, not your injected document.

Make sure resources are ready and the PDF uses the expected layout

page.pdf() uses print CSS by default. A page can therefore look different in the PDF than it does in a normal browser viewport, even when every URL resolves correctly. If the page uses screen-only styles, explicitly switch media type before printing; if the goal is print layout, leave the default in place. Puppeteer’s PDF guide documents waiting for fonts, but a setContent() workflow should still check the resources it actually depends on.

  • Wait for a page-specific selector or application-ready condition when scripts populate the content after initial loading.
  • For a required image, verify its load state and natural dimensions before calling page.pdf().
  • If fonts matter, wait for font readiness and inspect the rendered PDF, especially when fonts are external.
  • Use a deliberate navigation or content wait condition; a network-idle condition can be unsuitable for pages that keep connections open.
  • Inspect the output PDF rather than relying only on a successful page.pdf() call. A file can be generated while an asset is missing or a layout differs from expectation.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot missing images, styles, and links

The image or stylesheet is missing

Inspect the final resolved request URL first. Confirm the base path, including a trailing slash where needed, and check whether the reference begins with a slash. Then verify that Chromium can reach the address and that the response actually contains the resource. If the asset has a different root from the document links, give that asset an absolute URL rather than changing the shared base to suit only one reference.

A relative link points to the wrong directory

Check the exact base and path combination. A link such as details.html is resolved beneath a directory base; a root-relative value such as /details.html starts from the origin. If the intended link is on a different origin, write the complete destination into the anchor.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
NDYIN Portable Printers Wireless for Travel, N80 Bluetooth Thermal Printer
  • Wireless Bluetooth Printer: Portable thermal printer compatible with iPhone, Android phones, iPad and tablet computers via Bluetooth. For smartphones, please download the "Nada Print" App. You can also connect to laptops and computers for printing using a USB-C cable. (Note: Laptops and computers can only be connected via USB and require the installation of a driver first. Bluetooth connection is not supported.)
  • No-ink printing: Only supports US Letter and A4 size thermal paper.(Doesn't support regular paper) The no-ink portable thermal printer uses direct thermal technology, requiring no ink, toner or ribbons, making it environmentally friendly, cost-effective and time-saving. The thermal printer package comes with a roll of US Letter thermal printing paper. Note: When installing the paper, remember to switch the paper size switch on APP
  • Clear Print: NDYIN N80 portable thermal printer adopts high-definition printing technology, with a 203DPI resolution to provide you with clear printing results. This mobile printer is compatible with roll paper, folded paper and tattoo transfer paper, supporting printing from your mobile phone PDF, Word, pictures and web pages anytime and anywhere. It is recommended to use our NDYIN thermal paper to achieve good printing quality
  • Portable wireless printer for travel: The thermal printer is equipped with a built-in 1500mAh rechargeable battery, which can print 160 sheets of 8.5" x 11" thermal paper after being fully charged. It weighs only 1.5 pounds and is compact in size. This ink-free portable printer can be easily carried in a backpack or briefcase! It is perfect for business travel, cars, small offices, construction sites, schools and homes. You can print documents, contracts, invoices and boarding passes anytime and anywhere
  • The N80 thermal printer has a wide range of uses. The package includes the N80 printer, a roll of US Letter paper(7m/roll), a user manual, a guide card, a type-C soft cable and a type C adapter. Note: The charging adapter is not included. Special thermal paper is required for use; ordinary paper cannot be used. This ink-free portable thermal printer is suitable for various scenarios such as home, school, travel, office, and outdoor, meeting the printing needs of different groups of people. This tattoo template printer is also compatible with tattoo transfer paper, making it an ideal choice for tattoo art

The request is correct but the PDF still looks wrong

Check the generated PDF’s print layout, not just the screen rendering. Print CSS can hide elements, alter dimensions, or change colors. Also check that asynchronous content and required assets finished loading before the PDF call. Waiting for generic network idleness alone may not represent application readiness.

The PDF opens, but its links are not clickable or go somewhere unexpected

Do not assume every combination of Puppeteer, Chromium, and PDF viewer preserves clickable links identically. The official PDF references describe printing but do not establish a universal hyperlink-annotation guarantee. Generate a PDF with the exact Puppeteer and Chromium versions you deploy, then test the link destinations in the viewers your users rely on. A reported link-rendering issue from 2019 is historical and anecdotal, not a statement of current behavior.

Choose the right fix for your input

Situation Best starting point Why
HTML string passed to setContent() with a shared asset and link root Add <base href="…"> Sets the document base for relative references.
Generated markup with references that need explicit or different roots Resolve references to absolute URLs Makes each final destination explicit.
Page already served at its intended address Use page.goto(), then page.pdf() Loads the document from its actual URL before printing.
PDF workflow depends on clickable destinations Test the output with deployed versions and target viewers Do not rely on an assumed cross-version, cross-viewer guarantee.

Or skip the browser setup

If your goal is a clean screenshot of a public webpage rather than a Puppeteer PDF of injected HTML, ScreenshotNeo offers a one-request screenshot API. It does not resolve relative URLs in your custom Puppeteer HTML; it is an alternative when capturing a reachable page is the actual task. The request and available options are documented at ScreenshotNeo’s API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each removal step can be turned off.
  • Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients.
  • The free plan includes 1,000 screenshots a month with no card required; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.

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

Quick Recap

Bestseller No. 3
HP Smart Tank 5000 Ink Tank Printer | 2 Years of Ink Included | All-in-One
HP Smart Tank 5000 Ink Tank Printer | 2 Years of Ink Included | All-in-One
PREMIUM SUPPORT - Strong technical expertise to solve issues faster; THE LAST PRINTER YOU'LL EVER NEED. Enjoy years of refillable, cartridge-free printing.
$194.03

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.