RU

Go PDF Renderer

Focus
Backend / Infrastructure
Year
2026
Stack
Go · HTTP · chromedp / CDP · OpenAPI · Prometheus · Docker
GitHub repository

Engineering problem

Turn a heavyweight browser renderer into a small service with predictable resource use and secure defaults.

My role

Backend / Go Engineer. I designed the HTTP contract, concurrency model, Chrome lifecycle, resource policy, observability, tests and container deployment.

Request path

HTTP → strict decode & validation → bounded queue → isolated Chrome tab → CDP PrintToPDF → bounded PDF response

Outcome

A containerized OpenAPI service with health/readiness probes, Prometheus metrics, structured logs, graceful shutdown and automated tests around API, race and security boundaries.

Go PDF Renderer is a small HTTP service built around a deliberately constrained browser runtime. The core problem was not generating a PDF — Chrome already does that well — but making HTML rendering predictable as a service: bound CPU-heavy browser work, propagate cancellation and timeouts, validate input strictly, prevent submitted HTML from becoming an unrestricted network client, and expose enough operational state to run the service confidently.

Bounding an expensive browser runtime

Chrome is started once and reused instead of paying browser startup cost for every request. Each render gets its own tab, while the HTTP layer uses a semaphore to cap active renders and a separate queue timeout to reject work when the service is saturated rather than allowing an unbounded backlog to grow.

Request cancellation and render deadlines are bridged into the tab context, so abandoned or timed-out requests tear down their browser work. The service also bounds request-body and generated-PDF sizes, normalizes rendering options before execution, and verifies the result before returning it to the client.

Treating submitted HTML as hostile input

Remote network access is denied by default. When external assets are required, Chrome Fetch interception allows only explicitly configured hostnames; private, loopback, link-local and other non-public IP targets remain blocked. JavaScript execution is disabled unless the deployment explicitly opts in.

The HTTP boundary follows the same approach: strict JSON decoding rejects unknown fields, filenames are sanitized, API keys are compared in constant time and CORS origins are explicit. The design assumes HTML is active input and keeps permissive behaviour opt-in rather than default.

Operational by default

The service exposes liveness, Chrome readiness, version/build information, Prometheus metrics, the OpenAPI contract and an embedded Swagger UI. Structured JSON logs and request IDs make failures traceable without coupling the rendering layer to a larger application framework.

Deployment uses a multi-stage image with a static Go binary, Chromium Headless Shell, a non-root runtime user and a container healthcheck. SIGINT/SIGTERM trigger bounded graceful HTTP shutdown before the long-lived browser process is closed.

Testing the boundaries

The repository includes unit tests for API and option validation, race checks, explicit security-policy tests and an opt-in browser integration suite. CI, Docker/Compose setup, an OpenAPI 3.1 contract and documented threat-model assumptions make the project useful as a complete service rather than only a code sample.