Guides

Chrome HTML Document to PDF: Real Costs and Production Issues

Convert Chrome HTML documents to PDF. Understand headless Chromium cold starts (7.8s), web font failures, and SSRF risks. Practical code examples.

M Mikel Rougstone · 8 September 2026 · 8 min read
Chrome HTML Document to PDF: Real Costs and Production Issues

Converting a Chrome HTML document to PDF in a production environment is a nuanced task, often fraught with performance and security challenges. A common approach involves using headless Chromium, which can deliver a median render time of 0.6 seconds when properly managed, but incurs a significant cold start penalty without optimization.

The True Cost of Headless Chromium: Cold Starts and Resource Management

Operating headless Chromium for PDF generation involves more than just installing a package. The primary performance bottleneck for on-demand rendering is the cold start. A fresh headless Chromium instance can take approximately 7.8 seconds to initialize and process the first request. This latency is unacceptable for user-facing applications requiring immediate PDF output, such as receipts or invoices.

To mitigate cold start penalties, a persistent, "warm" browser instance is necessary. Keeping the browser warm reduces the render time significantly, bringing it down to approximately 0.65 seconds per document. This involves maintaining a pool of ready-to-use browser processes, which adds complexity to infrastructure management.

Consider the memory footprint. Each Chromium instance can consume hundreds of megabytes of RAM, scaling linearly with concurrent requests. For an application processing 100 documents in a batch call, memory requirements can quickly escalate, demanding robust server resources. This is particularly relevant for services that offer a batch rendering feature for up to 100 documents in one API call.

Managing Chromium in Production: Local vs. Hosted Solutions

For a handful of documents a day, typically less than 50 documents a month, a local library like Playwright or Puppeteer might be sufficient. These tools provide direct programmatic control over a headless browser. However, scaling this setup involves managing browser binaries, dependencies, and process lifecycle within your application environment. For instance, running sync_playwright directly within a Django web request handler will raise a SynchronousOnlyOperation error unless the rendering process is explicitly moved to its own thread or an asynchronous worker.

Here's a basic Python example using Playwright to render a local HTML file to PDF, highlighting the synchronous nature:

from playwright.sync_api import sync_playwright

def render_html_to_pdf_local(html_content, output_path):
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.set_content(html_content)
        page.pdf(path=output_path)
        browser.close()

# Example usage (would block a Django request without threading)
# html_doc = "<h1>Hello, PDF!</h1><p>This is a test.</p>"
# render_html_to_pdf_local(html_doc, "output.pdf")

This local approach demands constant maintenance, including security patches for Chromium, dependency updates, and handling unexpected browser crashes. In contrast, a hosted API like PDFGeny abstracts away these operational complexities, offering a median render time of 0.6 seconds and managing the underlying infrastructure.

Security Implications: SSRF and Input Validation

A URL-to-PDF endpoint introduces a significant Server-Side Request Forgery (SSRF) vulnerability if not properly secured. An attacker could provide a malicious URL that points to internal network resources, metadata endpoints (e.g., AWS IMDS), or other sensitive services. The server, in attempting to render the "document," would inadvertently make requests to these internal resources, potentially exposing sensitive information or enabling further attacks.

Robust input validation is paramount. Before fetching any URL, the application must resolve the host and explicitly reject private, loopback, and metadata addresses. This requires careful parsing of the URL and validation against known private IP ranges (e.g., RFC 1918 addresses like 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, and loopback 127.0.0.0/8).

For example, in Python, one might use the urllib.parse and socket modules:

import urllib.parse
import socket

def is_private_ip(ip_address):
    private_nets = [
        "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"
    ]
    # Simplified check, a full implementation would use ipaddress module
    # or a more robust library to check against CIDR blocks.
    # This example demonstrates the concept.
    return ip_address.startswith("10.") or ip_address.startswith("172.16.") or \
           ip_address.startswith("192.168.") or ip_address.startswith("127.")

def validate_url_for_pdf_rendering(url):
    parsed_url = urllib.parse.urlparse(url)
    if not parsed_url.hostname:
        raise ValueError("Invalid URL: No hostname found.")

    try:
        ip_address = socket.gethostbyname(parsed_url.hostname)
        if is_private_ip(ip_address):
            raise ValueError(f"SSRF risk: Attempt to access private IP {ip_address}")
        # Add more sophisticated checks for metadata endpoints, etc.
    except socket.gaierror:
        raise ValueError(f"Could not resolve hostname for {parsed_url.hostname}")
    return True

# Example usage
# try:
#     validate_url_for_pdf_rendering("http://localhost/admin")
# except ValueError as e:
#     print(e) # SSRF risk: Attempt to access private IP 127.0.0.1

PDFGeny addresses this by implementing stringent URL validation and isolation measures, protecting its infrastructure and user data from such attacks. This offloads a critical security burden from developers.

Web Fonts and Rendering Fidelity: A Silent Failure Mode

One subtle but common issue when converting HTML to PDF using headless browsers is the handling of web fonts. Developers often assume that if a web font renders correctly in a live browser, it will also appear correctly in the PDF. This is not always the case.

Web fonts silently fall back to system defaults in PDFs when the renderer finishes processing the document before the font asset has fully loaded. This is particularly problematic with large font files, slow network conditions, or when the rendering engine prioritizes speed over waiting for all external resources. The result is a PDF that looks visually different from the intended design, with incorrect typography. There is typically no error message, making this a difficult issue to debug. PDF Generator API: Real Costs & Performance in Production explores similar performance-related rendering nuances.

To avoid this, ensure that web fonts are preloaded or that the rendering engine has sufficient time to fetch and apply them. Alternatively, consider embedding fonts directly into the HTML using @font-face rules with base64 encoded font data for critical documents, though this increases HTML payload size.

The wkhtmltopdf Dilemma: Unmaintained, Not Dead

The conventional wisdom often states that wkhtmltopdf is 'dead'. This isn't entirely accurate. wkhtmltopdf is not 'dead' in the sense that it still functions for many basic HTML-to-PDF conversions. However, it is definitively unmaintained. The last significant update to its underlying rendering engine (WebKit) was years ago, meaning it lacks support for modern CSS features like Flexbox, Grid, and many advanced CSS3 properties.

The real cost of using wkhtmltopdf today is the CSS it never learned. Developers spend significant time writing compatibility stylesheets or completely redesigning layouts to accommodate its limitations. This translates directly into increased development time and ongoing maintenance overhead. For applications requiring modern, pixel-perfect PDF layouts, wkhtmltopdf is simply not a viable option. For a deeper look into this, consider reading wkhtmltopdf: The Unmaintained Renderer's Real Costs and Alternatives.

Headless Chromium, by contrast, provides a modern, up-to-date rendering engine that supports the latest web standards, ensuring greater fidelity between HTML and PDF output. PDFGeny utilizes headless Chromium as its default engine, alongside WeasyPrint for specific use cases and Ghostscript for PDF/A-2b compliance, offering a robust and modern rendering pipeline.

What We Got Wrong / What Surprised Us

Our initial assumption was that the biggest challenge with headless Chromium would be simply keeping the process alive and managing memory. While those are indeed significant, the most surprising and persistent issue turned out to be the subtle failures of web font loading. We expected a clear error message or a blank space if a font failed to load. Instead, the renderer would silently fall back to a default system font, producing a PDF that looked "off" but wasn't immediately identifiable as a font loading failure. Debugging these discrepancies required pixel-by-pixel comparisons and extensive logging of network requests during the rendering process to confirm font asset delivery times. This silent fallback added considerable time to template development and debugging cycles.

Practical Takeaways

Budget for Cold Starts or Use a Hosted API: If self-hosting headless Chromium, allocate approximately 7.8 seconds for the first render request on a cold instance. For real-time applications, implement a warm pool or opt for a hosted API like PDFGeny, which achieves a median render time of 0.6 seconds due to warmed instances.

  • Expected Outcome: Reduced latency for end-users, especially for first-time PDF generation requests.
  • Difficulty: Medium (self-hosting), Low (API).
  • Time Estimate: 1-2 days (self-hosting setup), 1-2 hours (API integration).

Implement Strict SSRF Protections for URL-to-PDF: For any endpoint accepting URLs for PDF conversion, validate the hostname and reject private, loopback, and metadata IP addresses. This is a critical security measure.

  • Expected Outcome: Prevention of internal network exposure and data breaches.
  • Difficulty: Medium.
  • Time Estimate: 4-8 hours for initial implementation and testing.

Preload or Embed Web Fonts for Critical Documents: To ensure consistent typography, either ensure web fonts are preloaded in your HTML or consider embedding them as base64 data. Monitor font loading times during development.

  • Expected Outcome: Visually consistent PDFs that match your web designs.
  • Difficulty: Low to Medium.
  • Time Estimate: 2-4 hours per template for optimization.

Choose Your Renderer Wisely: Recognize that wkhtmltopdf is unmaintained and lacks modern CSS support. For contemporary designs, headless Chromium is the superior choice. If PDF/A-2b compliance is required, ensure your solution supports it, as PDFGeny does with Ghostscript.

  • Expected Outcome: High-fidelity PDF output with modern CSS support, reduced development headaches.
  • Difficulty: Low (if choosing a modern engine), High (if trying to adapt old CSS to wkhtmltopdf).
  • Time Estimate: Saves days of debugging CSS compatibility.

Stop wrestling with Chromium containers and font issues. PDFGeny provides a hosted API that renders HTML to PDF with headless Chromium, WeasyPrint, and Ghostscript for PDF/A-2b. Send HTML, a URL or use one of our 40 ready document templates. Get started with 50 free documents a month, no card required.

Get a free API key

FAQ Section

What is the typical performance of converting Chrome HTML to PDF?

When using a fresh, cold headless Chromium instance, the initial render can take around 7.8 seconds. However, with a warmed instance or a hosted API like PDFGeny, the median render time significantly drops to approximately 0.6 seconds per document. PDFGeny also supports batch processing of up to 100 documents in one API call.

Can I convert a URL directly to PDF without downloading the HTML?

Yes, many tools and APIs, including PDFGeny, offer a URL-to-PDF conversion feature. You POST the URL to the https://pdfgeny.com/api/v1/render endpoint, and the service fetches and renders the content. However, robust security measures, such as SSRF protection, must be in place to prevent malicious URL exploitation.

What are the limitations of using wkhtmltopdf for modern PDF generation?

wkhtmltopdf is unmaintained and uses an outdated rendering engine, meaning it lacks support for modern CSS features like Flexbox and Grid. This often leads to significant development effort in creating compatible stylesheets or compromises in design fidelity. For contemporary web designs, a headless Chromium-based solution is preferable.

Does PDFGeny support specific PDF standards like PDF/A-2b?

Yes, PDFGeny supports outputting documents compliant with the PDF/A-2b standard, utilizing Ghostscript for this specific requirement. This is crucial for archival purposes where long-term readability and integrity of documents are paramount.

M

Mikel Rougstone

Founder, PDFGeny

I build and run PDFGeny — the API, the rendering fleet and the template catalog. Most of what I write here comes from something that broke in production first.