June 14, 2026?3 min

PDF generation in Node: Stop shipping headless browsers

Why I stopped shipping headless browsers for PDF generation and switched to a lightweight HTTP API approach ? less overhead, better scaling.

Node.jsArchitectureAutomation

I used to spin up Puppeteer for every PDF generation task. Invoice, receipt, report—headless Chrome every time. Then I realized I was shipping a 500MB dependency to fill a form.

The problem: headless browsers are overkill 90% of the time. You're not rendering complex layouts or JavaScript. You're filling a template with data.

Two years ago I switched to pdf-lib for in-process PDF manipulation. No browser, no external calls. Works great for simple cases—stamping text, flattening forms, adding signatures.

But then I hit the scaling wall. Processing 10,000 invoices concurrently? pdf-lib blocks the event loop. Single PDF takes 200ms, multiply that across requests.

Now I use an HTTP API approach. I POST the template and data, get back the filled PDF. Looks like waste at first—extra network call, separate service. But here's the win:

Decoupling. Your Node app doesn't care how the PDF gets made. Could be pdf-lib, LibreOffice, a Ruby service, whatever. Change implementations without touching application code.

Scale independently. PDF service runs on different hardware. CPU-heavy? Spin up more instances without bloating your main app.

Reusability. Every microservice that needs PDFs uses the same endpoint. I've done this across ERPNext and Odoo integrations—centralized, battle-tested.

Node 18+ made this elegant. fetch and FormData are built-in. One POST call, zero npm dependencies:

const form = new FormData();
form.append('template', 'invoice');
form.append('data', JSON.stringify(orderData));

const response = await fetch('http://pdf-service/render', {
  method: 'POST',
  body: form
});

const pdf = await response.arrayBuffer();

Same code runs on Lambda, Workers, or bare metal. No Puppeteer bloat. Clean.

Choose pdf-lib if you're filling one PDF in a batch job. Choose HTTP services if you need throughput or want to keep concerns separated. I lean HTTP now—it's paid for itself in maintainability.