Docker-first Envoy edge Fast API key Cache + Rate limit

Turn HTML into PDF, reliably.

A small Go microservice that renders raw HTML or a URL to PDF via headless Chromium, fronted by Envoy and backed by PostgreSQL (tokens) + Redis (cache/limits). Ship it as a container and keep the server clean.

Defaults: 30s timeout, margins in inches, output served as application/pdf.

Local smoke test
POST HTML → PDF
curl -k -H "X-API-Key: <your-api-key>" \\
-X POST "https://localhost/api/v0/pdf" \\
--form-string 'html=<h1>Hello PDF</h1>' \\
-F "format=A4" -F "orientation=portrait" -F "margin=0.4" \\
-o out.pdf
                    

What it does

A pragmatic HTML → PDF service: Envoy sits at the edge, the Go/Fiber app renders via headless Chromium, Redis holds short-lived cache + rate-limiter state, and PostgreSQL stores API tokens (with per-token limits).

HTML & URL input
Use POST /api/v0/pdf for raw HTML or GET /api/v0/pdf?url=… for remote pages.
Preloaded Chrome tabs
Optional Chrome pool keeps tabs warm. Faster throughput, less startup overhead.
Redis cache
Same HTML/URL + same parameters → served instantly from cache (if enabled).
Rate limiting + tokens
Token policies live in PostgreSQL, rate limiting state in Redis. Envoy handles edge routing so the app stays focused on rendering.
File logging
Rotating logs via lumberjack. In Docker Compose, ./logs is mounted by default.
Simple knobs
Paper size, orientation, margin, filename. That’s it. The service stays boring.

Demo

This page calls the API via Alpine.js (through Envoy). The public demo runs with a shared key and a conservative rate limit, so you can try it without signing up. (When self-hosting, you’ll use your own API key and token limits from PostgreSQL.)

Generate a PDF
Used as GET /api/v0/pdf?url=…
Used as POST /api/v0/pdf with multipart form-data. Loaded:
Valid range: 0.1 … 2.0
Must end with .pdf
Example curl (uses a placeholder key; the demo key is not shown here)
Result
Notes
  • Margins are inches (matches Chromium’s PDF print API).
  • Timeout is enforced server-side (default: 30s).
  • Cache uses a hash of HTML/URL + format + orientation + margin.
  • Chrome pool speeds things up, but needs enough resources for concurrent renders.

Quickstart (self-host)

Clone, run, tail logs. No extra server setup required beyond Docker + Compose.

1) Clone
git clone git@github.com:aplgr/html2pdf-service.git
cd html2pdf-service
2) Run
docker compose up --build
Logs are mounted to ./logs by default.
tail -f ./logs/html2pdf.log
3) Use
GET (URL → PDF)
curl -k -H "X-API-Key: <your-api-key>" \\
  -X GET "https://localhost/api/v0/pdf?url=https://example.com&format=A4&orientation=portrait&margin=0.4" \\
  -o page.pdf
POST (HTML → PDF)
curl -k -H "X-API-Key: <your-api-key>" \\
  -X POST https://localhost/api/v0/pdf \\
  --form-string "html=<h1>Hello</h1>" \\
  -F "format=A4" -F "orientation=portrait" -F "margin=0.4" \\
  -o page.pdf
Docker Compose notes
  • Envoy is the public entrypoint in the compose stack and forwards /api to the Go app.
  • Redis is internal to the compose network (cache + rate limiter state).
  • PostgreSQL runs as a dedicated container and stores API tokens (and per-token limits).
  • Chrome pool can be tuned via pdf.chrome_pool_size in config.docker.yaml.

API reference

Endpoints and parameters for PDF generation. In the public demo you can try requests without a key (limited per IP+User-Agent). When you provide X-API-Key, per-token limits apply (tokens are stored in PostgreSQL).

POST /api/v0/pdf

Multipart form-data:

Field Type Required Notes
html string yes Raw HTML. Must be at least 10 chars.
format string no One of the supported paper sizes (see config).
orientation string no portrait (default) or landscape
margin float no Inches. Range: 0.1 … 2.0 (default: 0.4)
filename string no Must end with .pdf; limited charset

curl -k -H "X-API-Key: <your-api-key>" \\
  -X POST https://localhost/api/v0/pdf \\
  -F "html=@./invoice.html" \\
  -F "format=A4" -F "orientation=portrait" -F "margin=0.4" \\
  -o invoice.pdf
GET /api/v0/pdf?url=…

Query parameters:

Param Type Required Notes
url string yes Must be http(s). Rendered in Chromium.
format string no One of the supported paper sizes (see config).
orientation string no portrait (default) or landscape
margin float no Inches. Range: 0.1 … 2.0 (default: 0.4)
filename string no Must end with .pdf; limited charset

curl -k -H "X-API-Key: <your-api-key>"\\
  -X GET "https://localhost/api/v0/pdf?url=https://example.com&format=A4&orientation=portrait&margin=0.4" \\
  -o page.pdf
Responses
  • 200 → PDF bytes with Content-Type: application/pdf
  • 400 → validation errors (missing/invalid html/url, bad format, etc.)
  • 408 → render exceeded timeout
  • 413 → HTML/PDF exceeds configured size limits
  • 429 → rate limit exceeded