October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Chromium

HTML to PDF in Go: Choosing a Renderer and Building a Reliable Pipeline

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

There is no single “best” HTML-to-PDF Go library. Choose a rendering architecture based on your HTML: a pure-Go renderer is easiest to deploy but may lack browser features; a Go binding to wkhtmltopdf uses a native Qt WebKit library; and a Go-driven Chromium process offers a modern browser engine at the cost of an operational browser dependency. The safest decision is to render representative invoices, reports, fonts, scripts and page breaks with each candidate before committing.

Three ways Go applications render HTML as PDF

Pure-Go rendering

gowkhtmltopdf documents static, no-cgo binaries and a Go API. Its README explicitly says it does not provide full CSS, JavaScript or Chrome parity: it supports a print-CSS subset and no JavaScript. The repository currently lists Go 1.26 or newer as a requirement. That makes it attractive for minimal containers and straightforward, static documents, but you must test every production template.

The project’s getting-started documentation identifies release 0.2.6. Its LibraryVersion value, 0.12.7-dev, is a wkhtmltopdf settings-surface compatibility identifier, not the project’s release number. Keep those values separate in build and support documentation.

wkhtmltox through a Go binding

The adrg/go-wkhtmltopdf binding calls the wkhtmltox library directly instead of launching the command-line executable. You must install the native wkhtmltox library, and conversion calls must run on the main thread. Its README also notes that the upstream project does not appear to be actively maintained, so treat maintenance and operating-system compatibility as explicit risks.

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

The wkhtmltopdf project describes its programs as headless Qt WebKit tools and licenses them under LGPLv3. Qt WebKit is not equivalent to a current Chromium browser; modern CSS and JavaScript behavior should never be assumed.

Chromium driven from Go

A browser-backed adapter can expose both wkhtmltopdf and Chromium through a common interface; one documented example uses Chromium through chromedp. This route executes a real browser engine and is usually the better fit for JavaScript-heavy applications, but the browser becomes an operational dependency. Verify the Chromium version, startup behavior, fonts, sandbox policy and resource limits in your deployment environment.

Decision table

Decision axis Pure-Go renderer wkhtmltox binding Chromium via Go
Browser fidelity Print-CSS subset; no JavaScript and no full Chrome parity according to the project documentation. Qt WebKit based; do not assume modern browser parity. Browser-backed rendering; validate the exact Chromium and chromedp versions you deploy.
Deployment Documents static, no-cgo binaries; Go 1.26+ is listed as required. Native wkhtmltox installation required; conversion has a main-thread constraint. Chromium packaging, sandboxing and process supervision are required.
Maintenance Inspect releases and issue activity at adoption time. Binding documentation flags concerns about upstream maintenance. Check chromedp and browser compatibility and release activity.
Best fit Static reports with controlled CSS and no scripts. Existing wkhtmltopdf-compatible templates and teams able to operate native libraries. Templates that need JavaScript, current CSS, web fonts or browser-like behavior.

How to choose with a representative test

Do not choose from a feature checklist alone. Build a fixture set containing the hardest document your service will produce.

  • A long invoice or report with predictable page breaks and repeating headers.
  • Grid or flex layouts, print-specific CSS, SVG, web fonts and high-resolution images.
  • JavaScript-generated totals or charts, including a deliberately slow API call.
  • Images loaded lazily, authenticated assets and a document containing non-Latin text.
  • Failure cases: a missing image, a navigation timeout, an invalid certificate and a page that never finishes scripting.

Compare page count, clipping, font substitution, links, selectable text, image quality and PDF metadata. Record cold-start time and memory under your real concurrency. No comparable benchmark establishes a universal speed winner, so publish your own measurements if performance is a deciding factor.

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

A practical Go implementation using wkhtmltopdf

If your application already depends on wkhtmltopdf-compatible HTML, a small Go wrapper around the executable is easy to reason about and keeps the conversion boundary explicit. Install a wkhtmltopdf build appropriate for your operating system, then make the binary path configurable.

package main

import (
    "context"
    "fmt"
    "os"
    "os/exec"
    "time"
)

func render(ctx context.Context, binary, input, output string) error {
    cmd := exec.CommandContext(ctx, binary,
        "--enable-local-file-access",
        "--encoding", "utf-8",
        "--print-media-type",
        input,
        output,
    )
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Run(); err != nil {
        return fmt.Errorf("wkhtmltopdf: %w", err)
    }
    return nil
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
    defer cancel()
    if err := render(ctx, "wkhtmltopdf", "invoice.html", "invoice.pdf"); err != nil {
        panic(err)
    }
}

--enable-local-file-access is intentionally explicit. Remove it unless the template genuinely needs local assets, and prefer serving controlled assets over allowing arbitrary filesystem paths. For untrusted input, do not pass a user-supplied URL directly to a server-side renderer: that can create server-side request forgery (SSRF). Use an allowlist, isolate network access, and block private address ranges.

Producing HTML safely

Keep data and markup separate. Escape interpolated text, generate absolute asset URLs when the renderer runs in a container, and include print rules such as @page, break-inside and page-break-after. If JavaScript is required, a non-JavaScript renderer cannot be made equivalent by adding a longer delay; use Chromium or pre-render the data on the server.

Chromium-specific operational choices

With Chromium, decide how browsers are started and reused. A single long-lived browser with isolated contexts can reduce startup overhead, while a fresh process per job improves fault isolation. Set a navigation timeout, wait for a deterministic selector (for example, [data-pdf-ready]) rather than an arbitrary sleep, and close pages even when rendering fails.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Package the exact browser binary or pin the image that supplies it.
  • Install every font used by your templates; missing fonts change line wrapping and pagination.
  • Apply a sandbox policy appropriate to your container rather than disabling security blindly.
  • Limit page count, HTML size, image dimensions and concurrent jobs to prevent memory exhaustion.
  • Log renderer version, URL or template identifier, elapsed time and the final error category.

Reliability and cost considerations

Timeouts and retries

Use separate limits for navigation, resource loading and total job duration. Retry transient browser crashes with a fresh page or process, but do not retry deterministic HTML errors indefinitely. Make jobs idempotent so a retry cannot duplicate an invoice or webhook.

Fonts, assets and determinism

Self-host fonts and version your CSS where possible. External analytics, ads and third-party widgets introduce nondeterminism and can delay completion. Block or remove them in the HTML used for PDF generation.

Resource planning

Measure peak resident memory, not only average latency. Browser processes, large images and concurrent PDF buffers can exhaust a small container. Queue work and cap concurrency based on those measurements. The available evidence does not establish a cross-engine performance or cost winner.

Troubleshooting

Blank or partially rendered PDF

Check that the input URL is reachable from the renderer, that the HTML has a valid character encoding, and that required assets are not blocked by authentication or network policy. For JavaScript pages, wait for a readiness selector or move to Chromium.

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

Missing CSS, images or fonts

Use absolute URLs or a controlled asset host, confirm certificates and DNS inside the runtime, and install the required fonts. If local files are necessary, grant access only to a dedicated asset directory.

Content is clipped or pagination is wrong

Set an explicit paper size and margins, add print media rules, and test tables and flex layouts across page boundaries. A renderer with limited modern CSS support may require a simpler print stylesheet.

wkhtmltox binding hangs or fails intermittently

Ensure conversion runs on the required main thread, confirm the native library version and architecture, and serialize or correctly schedule calls according to the binding’s requirements. Capture stderr and return the native error to your job logs.

Chromium fails in a container

Verify the browser binary exists, required shared libraries and fonts are installed, and the sandbox configuration matches the container’s privileges. Record the browser version and test with the same image used in production.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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 is a website screenshot API and MCP server for developers. It accepts a URL and returns PNG, JPEG, WebP or PDF. Before capture it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.

For a one-off PDF or image request, use the API documented at https://screenshotneo.com/docs/:

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

The same request from Go is:

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
)

func main() {
    q := url.Values{}
    q.Set("access_key", "YOUR_API_KEY")
    q.Set("url", "https://stripe.com")
    resp, err := http.Get("https://api.screenshotneo.com/v1/shot?" + q.Encode())
    if err != nil { panic(err) }
    defer resp.Body.Close()
    if resp.StatusCode >= 300 { panic(fmt.Errorf("HTTP %s", resp.Status)) }
    f, err := os.Create("shot.webp")
    if err != nil { panic(err) }
    defer f.Close()
    if _, err := io.Copy(f, resp.Body); err != nil { panic(err) }
}

Python:

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("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}`);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. Every feature is on every plan: 1,000 screenshots per month free with no card, then $5 for 3,000; yearly billing gives two months free. Create a free ScreenshotNeo account to try it.

FAQ

Is wkhtmltopdf still a modern browser?

No. It uses Qt WebKit, so validate current CSS and JavaScript requirements instead of assuming Chromium behavior.

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

Can a pure-Go renderer execute JavaScript?

The gowkhtmltopdf capability table says no; it supports a print-CSS subset rather than full JavaScript and Chrome parity.

Which engine should generate highly interactive pages?

Start evaluation with Chromium, then verify startup, fonts, sandboxing and resource limits in your target environment.

The Bottom Line

For static, controlled templates, a pure-Go renderer can simplify deployment. For existing wkhtmltopdf templates, use the native binding only when its installation and thread constraints are acceptable. For JavaScript-heavy or browser-dependent documents, drive a pinned Chromium runtime—and test real documents before selecting any engine.

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.

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

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
Crashes, No Sound, or Screen Glitches?Free driver scan

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.