Templates / Fonts & images
Fonts & images
Templates can use fonts and images either embedded in the HTML or fetched from a URL at render time. Both work; embedding is more reliable.
Two ways to include an asset
Embed it (recommended)
A data: URI carries the asset inside the document, so nothing is fetched over the network. This
is the most reliable option: the render cannot fail on a slow or unreachable host, and the output
is identical on every machine.
<img src="data:image/png;base64,iVBORw0KGgoAAAANS..." alt="Logo" />
<style>
@font-face {
font-family: 'Inter';
src: url('data:font/woff2;base64,d09GMgABAAAA...') format('woff2');
}
</style>Reference it by URL
You can also point at an external image, stylesheet, or font:
<link rel="stylesheet" href="https://fonts.example.com/inter.css" />
<img src="https://cdn.example.com/logo.png" alt="Logo" />External URLs are fetched server-side during the render, which is why the rules below apply.
Supported formats
- Images: PNG, JPEG, GIF, WebP, and SVG (Chromium also handles AVIF, ICO, and BMP).
- Fonts: WOFF, WOFF2, TTF, and OTF, via
@font-faceor a linked stylesheet.
The render waits for sub-resources to load before printing, so images and fonts are present in the output rather than half-loaded.
Rules for external resources
Because external URLs are fetched by our servers, a few rules keep that safe and bounded:
- Public HTTP(S) only. Each URL's host is resolved, and every resolved address must be
public. Private, loopback, link-local, and cloud-metadata addresses are blocked, and only
http/httpsis allowed. Redirects are re-checked at every hop, so a public URL cannot redirect to a private one. - A 10-second timeout per resource. Any single font or image that takes longer than 10 seconds to fetch fails the render. The whole render also has a 30-second budget.
- One bad resource fails the call. If a resource is blocked, unreachable, or times out, the
generation fails with
render_failed(502) and the error message names the offending URL, so you know exactly what to fix. A failed call does not consume quota.
These rules are why embedding matters for fidelity: a data: URI never touches the network, so
it cannot be blocked, cannot time out, and renders the same everywhere. For documents that must
be byte-for-byte reproducible, inline your CSS and embed your assets so there is nothing to
fetch.
A reliable setup
For production templates, the most robust pattern is:
- Inline your CSS in a
<style>tag rather than linking a stylesheet. - Embed logos, signatures, and icons as
data:URIs. - Embed fonts with
@font-faceand adata:src, or rely on the common system fonts the renderer already has.
This removes every external dependency from the render path, which is the surest way to keep the preview, your first call, and your millionth call identical.