Web fonts in generated PDFs: why they fall back, and how to make them load
The PDF shows Times where the page showed your brand font. Causes — timing, CORS, formats, licensing — and the fix for each.
Updated 31 August 2026
A PDF that renders in the wrong font is the most common quality complaint in document generation. The page looked right in the browser; the file looks like 1998. Here is why.
Timing
Chromium starts the PDF the moment you ask. If the font is still downloading, the text is laid out in the fallback and that is what gets printed. Fix: wait for the network to go idle before rendering (PDFGeny does this by default), or in your own Playwright code call `await page.evaluate(() => document.fonts.ready)`.
CORS
Fonts are subject to cross-origin rules. A font served from a CDN without `Access-Control-Allow-Origin` loads fine in the page where it is hosted and fails when the renderer fetches your HTML from another origin. Fix: serve fonts from the same host as the HTML, or add the header on the font host.
Format
WOFF2 is supported by every modern renderer. Old `.eot` and `.svg` font formats are not, and a stylesheet that lists them first can still resolve — but a stylesheet that lists only them will fall back. Ship WOFF2 with WOFF as a fallback and nothing else.
Inline HTML has no base URL
When you POST raw HTML, relative URLs like `/fonts/brand.woff2` have nothing to resolve against. Use absolute URLs, or embed the font as a data URI in an `@font-face` rule — for one or two weights the size is acceptable and the render is fully self-contained.
System fonts on the render server
If your CSS says `font-family: "Segoe UI", sans-serif` and the server is Linux, there is no Segoe UI. The fallback is whatever fontconfig picks, often DejaVu Sans. Either ship the font as a web font, or specify a fallback you actually like (Liberation Sans is metric-compatible with Arial).
Licensing
Most commercial font licences distinguish web use from "document embedding". Generating PDFs embeds the font in the file. Check the licence; open fonts (Inter, IBM Plex, Source Sans) avoid the question.
A checklist
- WOFF2 + WOFF, absolute or data URIs
- Fonts on the same origin as the HTML, or CORS headers set
- Wait for fonts before rendering
- A fallback stack that looks acceptable on Linux
- Licence permits embedding
Try it: convert HTML to PDF in the browser, or get a free API key — 50 documents a month.
More guides
Writing HTML templates for PDF: a style guide (Jinja, Django, Handlebars)
Templates that render well as PDFs follow a few rules: fixed page geometry, no external state, defensive filters, print CSS. Examples in Jinja and Django syntax.
Issuing 5,000 course certificates in one afternoon
A spreadsheet of names, one landscape template, batch requests of 100, and signed links per student. The full workflow including naming, verification codes and delivery.
Generating receipts at checkout without slowing the checkout
The receipt PDF should never sit between the customer and the order confirmation. A pattern: confirm first, render asynchronously, attach when ready.
URL-to-PDF and SSRF: how a PDF renderer becomes an attack surface
A renderer that fetches URLs can be pointed at your internal network. The attacks, the defences, and what to demand from a PDF API vendor.