ScreenshotNeo

BlogHow-to

How to Fix Missing Fonts in Browserless Website Screenshots

Diagnose whether Browserless screenshots capture too early, web font requests fail, or a self-hosted image lacks a system font—and apply the right fix.

By the ScreenshotNeo team4 October 20268 min read

Missing fonts in a Browserless screenshot usually come from one of three causes: capture starts before web fonts finish loading, a remote font stylesheet or font file fails or behaves differently in headless mode, or a self-hosted browser image lacks a required system font. Identify which case applies before changing configuration.

For a remote web font, verify its requests and wait for the browser’s font readiness before taking the screenshot. For a self-hosted image that needs an operating system font, add that font to the image and rebuild it. Browserless documents waitForFonts for PDF generation, but not as a BAP screenshot option; use browser-side waiting logic for screenshots instead. Browserless BAP screenshot options · Browserless Docker guidance · Browserless PDF options.

1. Identify which font is missing

First determine whether the intended typeface is a web font delivered by the page or a system font expected from the browser’s operating system.

What the page expects Likely cause Where to start
A font loaded through CSS, often using @font-face The stylesheet or font asset failed, returned an unexpected response, or was not ready at capture time. Inspect network requests and browser console output, then check readiness.
A font installed on the operating system The browser runtime does not have that font installed. If self-hosting, inspect and extend the image. For hosted Browserless, you cannot modify its provider container.
The font appears only at another viewport or page state Responsive CSS, a late route transition, or dynamic content changes which styles apply. Reproduce with the same viewport and page state, and set the viewport before capture.

A system font and a web font are separate cases. Installing an operating system font will not fix a blocked or broken web-font request. Likewise, waiting longer cannot make a failed font request succeed.

2. Reproduce the capture consistently

  1. Use the same target URL, browser runtime, viewport, and page state that produce the bad screenshot.
  2. Set the viewport before navigation or capture when responsive styles might affect the font or layout. Browserless recommends setting the viewport before capture.
  3. Record the font family the page intends to use and whether it comes from a remote asset or the operating system.
  4. Inspect browser console messages and network requests for the declaring stylesheet and font file. Check status, response headers, redirects, and whether the response is actually a font.

Do not diagnose from the screenshot alone: fallback text can look like a font-installation problem even when the real cause is capture timing or a failed font request.

3. If the web font loads too late, wait in the page before capture

Wait for the relevant page state, then wait for the browser’s font set to settle before invoking the screenshot operation. The following is browser-side JavaScript: run it in the page context after navigation and any required application-specific readiness checks.

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.setViewport({ width: 1440, height: 1000 });

// Add an application-specific condition here if the page renders asynchronously.
await page.evaluate(async () => {
  await document.fonts.ready;
});

// Invoke the screenshot method supported by your installed Browserless SDK
// or API version only after the page-side wait completes.

The snippet shows the ordering, not a particular Browserless SDK method signature: BAP SDKs and REST APIs have different option shapes, so use the screenshot call documented for your installed version. document.fonts.ready is the browser-side readiness signal; it is not a documented BAP screenshot option named waitForFonts.

Prefer readiness conditions over fixed sleeps

A fixed delay such as setTimeout can be useful as a short, measured workaround for a known page behavior, but it is not a reliable substitute for checking readiness. It may waste time on fast loads and still be too short on slow ones. If the site hydrates or changes routes after initial navigation, wait for a site-specific selector or state as well as font readiness.

4. If the font request fails or differs in headless mode

Waiting only helps when the request can complete. If the font stylesheet or asset fails, returns HTML, is blocked, or returns an unsupported or unexpected format, fix that request path first.

  • Confirm the stylesheet containing @font-face loads successfully.
  • Confirm the font-file request completes and its response is a font asset, not an error page or redirect target.
  • Compare the request made by the Browserless headless browser with the request made by a successful browser session.
  • Check whether access controls, bot protection, cookie-dependent delivery, request headers, or user-agent-based content negotiation affect the response.
  • After correcting the request, still wait for document.fonts.ready before capture.

Browserless’s 2019 troubleshooting article described a particular font service returning WOFF2 to local Chrome but TTF to headless mode, and suggested trying a legitimate user agent. Treat this as a historical diagnostic example, not a universal explanation or guaranteed fix. Test the actual site and font provider. Browserless’s font troubleshooting article.

5. If a self-hosted image lacks a system font

Browserless says its self-hosted images include common fonts for Latin, CJK, Arabic, Thai, and Indic scripts. If your self-hosted deployment needs a font that is absent, Browserless documents extending the image with a custom Dockerfile.

  1. Check inside the browser runtime whether the required font is installed and available under the family name used by the page.
  2. Confirm the font’s license permits installation and redistribution in your image.
  3. Extend the Browserless image with the required font package or licensed font files. Package names depend on the base distribution.
  4. Refresh the font cache if required by that base image.
  5. Rebuild the image and verify font availability inside the resulting runtime, then repeat the capture.

There is no one package-install command that applies to every Browserless image: the base distribution, font package, and licensing terms determine the correct steps. This image customization applies to self-hosted deployments. A hosted Browserless customer should diagnose remote font loading and readiness, rather than trying to modify the provider’s container.

6. Use options for the capture route you actually call

Browserless documents different option shapes for BAP screenshots and PDF generation. The PDF documentation includes waitForFonts, which waits for document.fonts.ready. The BAP screenshot options list waitForImages, but do not list a screenshot-specific waitForFonts flag. Do not assume a PDF option is accepted for screenshots; verify against the documentation for the exact API or SDK version in use.

Capture route Font-wait guidance Important distinction
BAP screenshot Wait in page-side browser logic before invoking the screenshot. The documented screenshot options do not include waitForFonts.
PDF generation Browserless documents waitForFonts. This does not establish that the same option works for screenshots.

7. Troubleshooting common errors

Symptom Likely cause Fix
Screenshot consistently shows a fallback face, but a later manual capture is correct. Capture runs before fonts are ready. Wait for the page’s required state and evaluate document.fonts.ready before the screenshot call.
The font request is red or has a failing status. Network, access-control, URL, or server error. Fix the request and confirm the stylesheet and font asset load successfully from the Browserless runtime.
The font URL returns a document or error page instead of a font. Redirect, denied request, or provider response differs in headless mode. Inspect the final URL and response; compare with a successful browser request and test an appropriate user agent if evidence points to user-agent negotiation.
Only a self-hosted deployment lacks a particular local font. The runtime image lacks that system font. Extend and rebuild the image with a properly licensed font, then verify it inside the runtime.
Adding waitForFonts to a screenshot request has no effect or is rejected. A PDF option was applied to a screenshot route, or the SDK version uses another shape. Use the screenshot options for the installed version and do the font wait in page-side code.
Fonts and layout differ only at a narrow viewport. Responsive styles select another family, weight, or asset. Set the intended viewport before capture and inspect the active computed style and matching font requests.
A fixed delay sometimes works but fails under load. Font delivery time varies or the application renders after the delay. Replace the guessed delay with font readiness and a page-specific readiness condition.

8. Performance, reliability, and cost

Waiting for font readiness adds time only when the browser still has font work to complete, while a fixed sleep adds its full duration on every run. Readiness waits improve consistency but cannot repair a failed request, so report request failures separately from timeouts waiting for page state.

For repeatable captures, keep the viewport, browser runtime, headers, cookies, and page state consistent. A deployment that depends on self-hosted system fonts should include those fonts in the built image so fresh or restarted browser instances use the same inventory. No universal wait duration or success rate applies across sites, networks, and runtimes.

Browserless usage and pricing depend on the service and plan in use; this diagnosis does not establish a specific cost. Measure capture duration and retry behavior in your own route. Avoid retrying a permanently denied font request as though it were a transient timing issue.

9. Or skip the browser setup

If the goal is simply a clean website screenshot, ScreenshotNeo is a website screenshot API and MCP server. Its API accepts a URL in one GET request and returns PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Its MCP server provides screenshot, page-info, and PDF tools for AI agents. Free includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does waiting for document.fonts.ready guarantee that a font appears?

No. It indicates the browser’s font loading work has settled; it does not guarantee the intended asset loaded successfully or that the page selected the expected family.

Can a missing system font be fixed for hosted Browserless?

The custom-image approach is for self-hosted deployments. For hosted use, investigate whether the page uses a remote font, whether its request succeeds, and whether capture waits for rendering.

Should I use waitForImages for missing fonts?

It concerns images and does not replace checking font requests or waiting for font readiness.

Is changing the user agent always the solution?

No. Browserless’s 2019 article gives a specific example where it was worth trying. Compare actual responses and use this only when the evidence suggests different delivery by user agent.