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

Use a headless Chromium browser on the server, navigate to the URL, wait for the page to be ready, and call page.pdf(). In NestJS, put that workflow in an injectable service and return the resulting buffer from a controller. Puppeteer is the most direct fit; Playwright offers a similar page API but its documented PDF export is Chromium-only. The examples below cover print CSS, dynamic content, cleanup, security, deployment, and a managed alternative.

What the conversion actually does

A PDF is produced from the page as a browser renders it, not by downloading the raw HTML and changing its extension. The browser executes JavaScript, loads fonts and images, applies CSS, and then prints the resulting document. Puppeteer’s documented sequence is to launch a browser, open a page, navigate with page.goto(), call page.pdf(), and close the browser. The PDF API waits for fonts by default.

PDF generation uses print media. Rules inside @media print can therefore make the PDF look different from the screen. Set page size, margins, orientation, backgrounds, scale, page ranges, and headers or footers through the PDF options rather than trying to post-process the bytes.

Choose Puppeteer or Playwright

Concern Puppeteer Playwright
Basic operation page.goto() followed by page.pdf() page.goto() followed by page.pdf()
NestJS setup A NestJS integration package, nestjs-puppeteer, is available; verify its current launcher and version instructions. Use Playwright directly in a provider or service.
PDF engine Chromium-based browser launched by Puppeteer Documented export is Chromium-only
Print controls Format, width/height, margins, landscape, backgrounds, scale, page ranges, CSS page size, header/footer templates and a PDF timeout PDF uses print CSS; call page.emulateMedia({media: 'screen'}) first when screen styling is required

There is no published benchmark establishing a universal winner. Select the library whose browser binary, NestJS integration, print controls and operational model fit your deployment. The package listing for nestjs-puppeteer reports CI exercises with NestJS 10 and 11 and Puppeteer 23 and 24, but verify live compatibility before adopting it.

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

Install a minimal Puppeteer setup

Install Puppeteer in the NestJS project. Its package normally supplies a compatible browser; in containers, confirm that the binary and system libraries are present.

npm install puppeteer

The following service launches one browser for the application lifetime, creates a fresh page for each request, and closes the browser during shutdown. Reusing the browser avoids launching a process for every conversion while page isolation prevents cookies and DOM state leaking between jobs.

PDF service

import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import puppeteer, { Browser, PDFOptions } from 'puppeteer';

@Injectable()
export class PdfService implements OnModuleInit, OnModuleDestroy {
  private browser!: Browser;

  async onModuleInit() {
    this.browser = await puppeteer.launch({
      headless: true,
      args: ['--no-sandbox', '--disable-setuid-sandbox'],
    });
  }

  async onModuleDestroy() {
    await this.browser?.close();
  }

  async fromUrl(url: string): Promise<Buffer> {
    const page = await this.browser.newPage();
    try {
      await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
      await page.goto(url, { waitUntil: 'networkidle2', timeout: 45_000 });
      await page.waitForSelector('main', { timeout: 15_000 }).catch(() => undefined);
      await page.emulateMediaType('print');

      const options: PDFOptions = {
        format: 'A4',
        printBackground: true,
        margin: { top: '18mm', right: '14mm', bottom: '18mm', left: '14mm' },
        preferCSSPageSize: true,
        timeout: 45_000,
      };
      return Buffer.from(await page.pdf(options));
    } finally {
      await page.close();
    }
  }
}

The networkidle2 condition is useful for many pages but is not a guarantee that an application has finished rendering. Prefer an application-specific selector or readiness flag for dashboards and single-page apps. The selector wait above is deliberately optional; replace it with a required selector when its absence means the document is invalid.

Controller that returns the bytes

import { BadRequestException, Controller, Get, Query, Res } from '@nestjs/common';
import { Response } from 'express';
import { PdfService } from './pdf.service';

@Controller('pdf')
export class PdfController {
  constructor(private readonly pdf: PdfService) {}

  @Get()
  async create(@Query('url') url: string, @Res() res: Response) {
    if (!url || !/^https?:\/\//i.test(url)) {
      throw new BadRequestException('url must be an http or https URL');
    }
    const bytes = await this.pdf.fromUrl(url);
    res.set({
      'Content-Type': 'application/pdf',
      'Content-Disposition': 'inline; filename="page.pdf"',
      'Content-Length': bytes.length.toString(),
    });
    res.send(bytes);
  }
}

Register both classes in a module. In a real endpoint, validate and authorize the destination more strictly than the example. The browser makes requests from your server network, so unrestricted user-supplied URLs can reach internal services, cloud metadata endpoints, or private administration pages. Resolve DNS, reject private and loopback ranges, control redirects, and consider blocking dangerous schemes and subresources.

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

Control the PDF’s appearance

Print CSS

@page { size: A4; margin: 18mm 14mm; }
@media print {
  nav, .cookie-banner, .chat-widget { display: none !important; }
  .break-before { break-before: page; }
  a { color: #000; text-decoration: none; }
}

printBackground: true is required when background colors or images should appear; backgrounds are otherwise off by default. preferCSSPageSize: true lets an @page rule win over the format option. Use landscape: true for wide tables, scale to fit dense layouts, and pageRanges for selected pages. Header and footer templates can contain HTML and supported page-number classes, but they run in a restricted context, so keep them self-contained.

Screen styling instead of print styling

Playwright documents that PDF generation applies print CSS. If the intended output is the screen view, call await page.emulateMedia({ media: 'screen' }) immediately before page.pdf(). Chromium may alter colors for printing; CSS -webkit-print-color-adjust: exact requests the specified colors.

Dynamic content and assets

  • Wait for a stable application selector, not only network idleness, when JavaScript fills the page after the initial load.
  • Ensure images have loaded; lazy-loaded images may require scrolling or an application flag that disables lazy loading for print.
  • Use a bounded navigation timeout and a separate PDF timeout. A PDF timeout does not define how long navigation or application code may run.
  • Keep one page per job and close it in a finally block, even when navigation or PDF generation fails.

Production design and deployment

Browser lifecycle

Launch the browser during module initialization and close it during shutdown, as shown above. For high concurrency, add a bounded page or browser pool instead of creating unlimited tabs. Measure your own workload; the cited documentation does not provide throughput or memory benchmarks. If the browser process exits, detect the failure and recreate it rather than returning opaque errors forever.

Container requirements

Your image must contain a Chromium binary and its shared-library dependencies. Run the same launch command in the deployment image, not only on a developer laptop. The --no-sandbox flags are commonly needed in restricted containers but reduce isolation; prefer a correctly configured sandbox and an unprivileged user when your platform permits it.

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

Reliability controls

  • Set request, navigation, selector, and PDF timeouts.
  • Return a clear 4xx for invalid URLs and a 5xx for browser failures without exposing page contents or credentials.
  • Log a job identifier, target host, elapsed phases, and failure class; avoid logging authorization headers or full sensitive URLs.
  • For large documents, stream or queue work rather than holding many multi-megabyte buffers in memory.
  • Restrict outbound network access and decide whether redirects to a different host are allowed.

Troubleshooting common failures

“Could not find Chrome” or missing shared libraries

The runtime cannot access the browser binary or Linux dependencies. Install Puppeteer’s browser during the image build, use the launcher path configured by your platform, and verify the binary inside the running container.

The PDF is blank or missing dashboard data

Navigation completed before client-side rendering. Wait for a selector that appears only after data arrives, or expose a server-side readiness marker. Also check that the page did not return a bot challenge or an authentication redirect.

Fonts or icons look wrong

Use the correct font URLs and wait for them; Puppeteer’s PDF method waits for fonts by default. Check cross-origin permissions and ensure the font files are reachable from the server.

Colors or backgrounds differ from the browser

PDF output is print media and backgrounds are disabled unless requested. Add print rules, set printBackground: true, and use -webkit-print-color-adjust: exact where exact color reproduction matters.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Funny Coding I Know HTML How To Meet Ladies T-Shirt
  • Funny saying for any front-end developer, web developer, computer programmer, computer systems engineer, mobile app developer, software developer, or code lover who likes to code, make funny programming jokes, and take memorable photos.
  • Wear it proudly at International Programmers' Day, school, coding classes, or coding communities! It also makes a funny present for a computer programming lover friend.
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem

The request hangs

Long-polling, WebSockets, ads, or a page that never becomes idle can defeat networkidle2. Use a targeted readiness selector, abort or block nonessential requests, and enforce independent timeouts.

Private pages return a login screen

Pass authentication deliberately with a controlled cookie or header, or generate the PDF inside the authenticated application. Never accept arbitrary credentials from an untrusted caller, and do not let a URL parameter select internal hosts.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo exposes a website screenshot and PDF API, so your NestJS job can make one HTTP request instead of operating Chromium. Its PDF endpoint accepts the target URL and returns a rendered document; cookie or consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and whether the request was billed. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for AI clients.

See the ScreenshotNeo documentation for authentication and options. A direct call is:

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.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o page.pdf

In NestJS, proxy the response or save it as a job result:

Best Value
I Know HTML How To Meet Ladies Funny Programming Language T-Shirt
  • Programming Language Lover Code Apparel. App or Web Design and Development Expert Funny Dress. Best Valentines Idea For Coding Lover. HTML Code or Meaning Costume
  • Funny I Know HTML - How To Meet Ladies Computer Programmer Quotes
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem
const q = new URLSearchParams({ access_key: process.env.SCREENSHOTNEO_KEY!, url });
const upstream = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!upstream.ok) throw new Error(`ScreenshotNeo failed: ${upstream.status}`);
const pdf = Buffer.from(await upstream.arrayBuffer());

Python and Node.js equivalents are available when you need a separate worker:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());

The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Can NestJS convert an HTML string instead of a public URL?

Yes. Create a page, call page.setContent(html), wait for its fonts and application assets, then call page.pdf(). Apply the same untrusted-content and resource-loading controls as for a URL.

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

Does PDF generation work in every Playwright browser?

The cited Playwright export documentation specifies Chromium-only PDF generation. Design the deployment around Chromium when using that API.

What is the default paper format in Puppeteer?

The documented default is Letter. Set format: 'A4' or explicit width and height when your audience or print workflow requires another size.

Frequently Asked Questions

Can I generate a PDF from authenticated content?

Yes. Establish a controlled browser session with approved cookies or headers before navigation, and keep credentials outside user-controlled URL parameters.

Should I launch a browser for every request?

Usually no. Reuse a managed browser process and create isolated pages, with a bounded pool for concurrency and shutdown cleanup.

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

Quick Recap

Bestseller No. 2
SaleBestseller No. 4
Funny Coding I Know HTML How To Meet Ladies T-Shirt
Funny Coding I Know HTML How To Meet Ladies T-Shirt
Lightweight, Classic fit, Double-needle sleeve and bottom hem
$14.27
Bestseller No. 5
I Know HTML How To Meet Ladies Funny Programming Language T-Shirt
I Know HTML How To Meet Ladies Funny Programming Language T-Shirt
Funny I Know HTML - How To Meet Ladies Computer Programmer Quotes; Lightweight, Classic fit, Double-needle sleeve and bottom hem
$19.99

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.