October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
HTTP

How to Stream and Download wkhtmltopdf Output in Web Applications

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

Yes—you can send a wkhtmltopdf PDF directly to an HTTP response without creating a temporary file. Run wkhtmltopdf with - as its output path, copy stdout to the response, keep stderr for diagnostics, and check the exit status. Set Content-Type: application/pdf and use Content-Disposition: attachment when the browser should download the file (or inline to open it in the built-in PDF viewer).

This guide covers process streaming, the libwkhtmltox buffer, Node.js streams, download and inline behavior, security, timeouts, failure handling, and a practical production checklist. The examples assume wkhtmltopdf 0.12.6 with patched Qt, the documented command-line release.

Choose the output pattern first

Pattern How it works Memory and failure characteristics
Child-process stdout Invoke wkhtmltopdf ... - and pipe stdout to the HTTP body. Works incrementally and avoids a temporary file. Your framework must handle backpressure and process cleanup.
libwkhtmltox buffer Leave the library output setting empty, then copy the generated buffer to the response. Simpler where a complete byte array is required, but memory grows with PDF size.
Node.js wrapper stream Use the wrapper’s readable stream and pipe it after setting headers. Natural streaming; attach error and close handlers so a failed renderer cannot leave a hanging request.

wkhtmltopdf is a headless Qt WebKit command-line converter. It does not require an X display service. Pin the binary version in deployment and record it with your application because rendering can change when the executable changes.

HTTP headers: download or display

Force a download

Send:

Content-Type: application/pdf
Content-Disposition: attachment; filename="report.pdf"

The filename should be a fixed, validated value. Never place unsanitized user input directly in a response header.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
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

Open in the browser viewer

Use the same content type with:

Content-Type: application/pdf
Content-Disposition: inline; filename="report.pdf"

Some clients ignore inline display because of user settings, but the disposition communicates your intended behavior.

Stream wkhtmltopdf stdout from a web endpoint

The command-line output setting accepts - for stdout. Keep stdout exclusively for PDF bytes; diagnostics belong on stderr. A non-zero exit means the generated document must not be treated as a successful PDF.

Portable process algorithm

  1. Validate the requested URL and any rendering options.
  2. Set the PDF response headers before writing body bytes.
  3. Spawn wkhtmltopdf --quiet input-url -.
  4. Copy child stdout to the response stream while separately collecting or logging stderr.
  5. Enforce a process timeout and terminate the child if it exceeds the limit.
  6. After stdout closes, inspect the exit code. If it is non-zero and headers are not committed, return an error response; if headers were committed, abort the response and log the failure.

Python (standard library)

from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
import subprocess

class PdfHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        target = "https://example.test/"
        parsed = urlparse(target)
        if parsed.scheme not in ("http", "https"):
            self.send_error(400, "Unsupported URL")
            return

        proc = subprocess.Popen(
            ["wkhtmltopdf", "--quiet", target, "-"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        self.send_response(200)
        self.send_header("Content-Type", "application/pdf")
        self.send_header("Content-Disposition", 'attachment; filename="report.pdf"')
        self.end_headers()

        try:
            while True:
                chunk = proc.stdout.read(64 * 1024)
                if not chunk:
                    break
                self.wfile.write(chunk)
                self.wfile.flush()
            stderr = proc.stderr.read()
            code = proc.wait(timeout=90)
        except (BrokenPipeError, ConnectionResetError):
            proc.kill()
            proc.wait()
            return
        except subprocess.TimeoutExpired:
            proc.kill()
            proc.wait()
            return

        if code != 0:
            self.log_error("wkhtmltopdf exited %s: %s", code, stderr.decode(errors="replace"))

HTTPServer(("127.0.0.1", 8080), PdfHandler).serve_forever()

For production Python applications, use the equivalent streaming API in your framework and make the timeout, URL allow-list, and error response configurable. The example demonstrates the important separation between stdout and stderr; it is not a substitute for authentication, authorization, or a URL policy.

Node.js with a child process

import { spawn } from "node:child_process";
import http from "node:http";

http.createServer((req, res) => {
  const target = "https://example.test/";
  res.writeHead(200, {
    "Content-Type": "application/pdf",
    "Content-Disposition": 'attachment; filename="report.pdf"'
  });

  const child = spawn("wkhtmltopdf", ["--quiet", target, "-"], {
    stdio: ["ignore", "pipe", "pipe"]
  });
  let diagnostics = "";
  child.stderr.on("data", chunk => { diagnostics += chunk.toString(); });
  child.on("error", err => {
    if (!res.headersSent) res.writeHead(502, { "Content-Type": "text/plain" });
    res.destroy(err);
  });
  child.on("close", (code, signal) => {
    if (code !== 0) console.error({ code, signal, diagnostics });
    if (!res.destroyed) res.end();
  });
  req.on("close", () => { if (!res.writableEnded) child.kill("SIGTERM"); });
  const timer = setTimeout(() => child.kill("SIGKILL"), 90000);
  child.on("close", () => clearTimeout(timer));
  child.stdout.pipe(res);
}).listen(8080);

In a real service, decide whether an error can still change the status before calling writeHead. Once PDF bytes have been sent, an HTTP 500 status cannot replace them; destroy the connection and record the renderer diagnostics instead.

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⁴

cURL from a shell-backed endpoint

curl -sS --fail-with-body https://example.test/ 
  | wkhtmltopdf --quiet - -

That pipeline is useful for experimentation, but an application endpoint should pass a controlled URL as an argument, not concatenate untrusted shell text. Prefer an argument array (as in the Python and Node examples) to avoid shell injection.

Using libwkhtmltox without a temporary file

The library setting for the output path has two special behaviors: - sends output to stdout, while an empty value retains the generated PDF in an internal buffer. The buffer approach suits frameworks whose response API accepts a complete byte array.

  1. Create and configure the global and document settings.
  2. Set the document output path to an empty string.
  3. Convert the page.
  4. Read the library’s generated byte buffer and its length.
  5. Set Content-Type and Content-Disposition, then write exactly that many bytes.
  6. Free the buffer and destroy the converter even when conversion fails.

Because the entire document is resident in memory, impose a maximum page size or job limit. A large, image-heavy document can consume substantially more memory than its final compressed PDF size while WebKit renders it.

Rendering controls that affect streamed output

JavaScript and asynchronous pages

JavaScript is enabled or disabled by an explicit setting, and the documented JavaScript delay defaults to 200 milliseconds. A page that fills its content asynchronously may need a longer delay or a window-status condition; otherwise wkhtmltopdf can capture before the data appears.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
HP Smart Tank 5000 Wireless All-in-One Ink Tank Printer, Scanner, Copier with 2 Years of Ink Included, Best-for-Home, Cartridge-Free, Refillable and AI-Enabled. (5D1B6A)
  • 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.

Load errors

Choose deliberately among aborting, ignoring, or skipping load errors. Aborting is safer for reports that must be complete. Ignoring can produce a usable PDF when a non-critical asset fails, but it can also hide missing content.

Local files and resources

Local-file access and resource loading should be restricted. If a document needs local images or stylesheets, grant access only to the required directory rather than enabling broad filesystem access. Review custom headers, cookies, and user-agent values for the same reason: they can expose credentials to a renderer pointed at an attacker-controlled URL.

Page geometry

Set page size, margins, orientation, and other layout options explicitly when output must be stable across environments. Keep the binary and option set together in deployment documentation.

Security and reliability boundaries

  • SSRF: allow-list destination hosts and schemes; block loopback, link-local, private, and metadata-network addresses when users can supply URLs.
  • Credentials: never forward arbitrary cookies, Authorization headers, or custom headers from an untrusted request.
  • Resource exhaustion: cap conversion time, concurrent renderer processes, document size, and redirect behavior.
  • Isolation: run the renderer with a restricted OS user and filesystem permissions. Treat HTML, CSS, JavaScript, and remote resources as untrusted input.
  • Client disconnects: stop the child process when the response closes so abandoned requests do not consume a renderer slot.
  • Observability: log the pinned wkhtmltopdf version, exit code, elapsed time, target policy result, and stderr without logging secrets.

Common failures and fixes

Symptom Likely cause Fix
Browser downloads an HTML error page The application wrote an error after beginning a PDF response. Validate before committing headers; after commitment, abort and log rather than appending HTML.
PDF is blank or missing dynamic content JavaScript finished after capture. Increase the JavaScript delay or use a window-status condition.
Conversion hangs Remote resource, script, or renderer never completes. Apply a process timeout, kill the child, and cap redirects and external requests.
Images or stylesheets are absent Local-file access or resource loading is restricted, or the URL is inaccessible. Use an explicit, narrow local allow-list and verify resource URLs from the renderer’s network context.
Intermittent truncated responses Child process, client, or proxy closed early; stdout and stderr were mixed. Pipe only stdout, handle client-close events, and inspect exit status and stderr.
High memory usage Whole-document buffering or too many concurrent jobs. Stream stdout, limit concurrency, or move large jobs to a worker queue.

Testing and deployment checklist

  1. Pin wkhtmltopdf 0.12.6 patched Qt (or record the exact version you deploy).
  2. Test both attachment and inline dispositions in your supported browsers.
  3. Exercise successful pages, JavaScript-heavy pages, missing assets, non-zero exits, timeouts, and client disconnects.
  4. Verify that stderr never contaminates the PDF body.
  5. Load-test large PDFs and confirm backpressure, process cleanup, and memory ceilings.
  6. Check proxy and server limits for response time and PDF size.
  7. Review URL, cookie, header, and local-file policies before allowing user-supplied input.
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 goal is simply a clean screenshot or PDF of a web page rather than running wkhtmltopdf yourself, ScreenshotNeo provides a single HTTP call. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

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

Read the complete parameter reference in the ScreenshotNeo documentation. The following examples use the supplied API format:

Rank #4
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

cURL

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

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 available on every plan: 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Frequently Asked Questions

Can wkhtmltopdf write a PDF to stdout?

Yes. Pass - as the output argument, for example wkhtmltopdf --quiet https://example.test -, and keep stderr separate.

Should I stream or buffer the PDF?

Stream stdout when documents may be large and your framework supports backpressure. Use the library buffer when your response API requires a complete byte array and you can enforce memory limits.

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.

Why did my endpoint return a corrupt PDF?

Common causes are mixing stderr with stdout, writing an HTML error after PDF bytes, or ignoring a non-zero renderer exit. Keep channels separate and validate the exit status.

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.

Leave a Reply

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

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.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

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.