Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Chromium font failures in production usually come from one of four layers: the browser or Linux dependencies are missing, the required system font is absent, a web-font request fails, or the screenshot/assertion runs before used fonts finish loading. Identify the layer first, then install a version-matched browser, wait for document.fonts.ready, and verify the specific face and network request. Because Chromium rendering is platform-specific, compare the production image with the machine where the test passes.

Start by classifying the symptom

Do not treat every typography problem as a missing font package. The symptom narrows the investigation:

Symptom Most likely layer First check
Chromium will not start Browser binary, shared libraries, or version mismatch Run the Playwright browser install and enable DEBUG=pw:browser.
Text uses a visibly different typeface System font absent, web-font request failed, or capture happened too early Inspect the loaded face and wait for document.fonts.ready.
Boxes appear instead of some glyphs The required glyph font is unavailable or did not load Check the exact face, weight, style and font-file response.
Local passes but production fails OS, container image, browser build or deployment policy differs Record and compare runtime details before changing CSS.

The title alone does not identify a root cause. Capture evidence from the production runtime rather than installing an arbitrary font package.

Record the production runtime

Write down the operating system and base-image tag, whether execution is containerized, the Playwright package version from the lockfile, the Chromium version, and whether the design uses system fonts or remotely served web fonts. Chromium’s rendering is platform-specific, so a Linux container can legitimately render differently from macOS or Windows even with identical HTML and CSS.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Also record the exact failure: launch error, fallback typography, missing glyphs, a flaky visual assertion, or a screenshot taken before late-rendered content appears. This distinction prevents a browser-launch investigation from being confused with a page-level font problem.

Install a browser and Linux dependencies that match Playwright

Playwright expects browser binaries associated with its package version. Install the browser and operating-system dependencies in the same build or runtime setup that executes the tests:

npx playwright install --with-deps chromium

Run this after installing the project dependencies from the lockfile. Re-run it when upgrading Playwright, and avoid copying a browser binary from an unrelated image or workstation.

Using the official Docker image

Playwright’s official Docker images include browsers and their system dependencies. Pin an image tag compatible with your project instead of floating to an unspecified latest image. The Python image supplies the browser and dependencies but does not include the Playwright Python package; install that package in your application environment.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A container does not automatically contain every font your page references. If you rely on a system-installed family, verify that family exists in the image. If you use a web font, continue with request and readiness checks below.

Wait for used web fonts before screenshots or assertions

Navigation completion is not the same as font readiness. After the page state that displays the text is present, wait in the page context:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'after-fonts.png', fullPage: true });
await browser.close();

MDN Web Docs describes the readiness promise as fulfilling when loading and layout operations for all used fonts are complete (the page was updated August 27, 2026). That qualifier matters: a CSS-declared face that is not used by any rendered text may remain unloaded. Optional faces, dynamically inserted content and content rendered after navigation still require you to perform the wait after that state exists.

Check the face your test actually needs

Use the FontFaceSet to inspect declarations and statuses. The test below returns each face’s family, style, weight and status:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const faces = await page.evaluate(() =>
  [...document.fonts].map(font => ({
    family: font.family,
    style: font.style,
    weight: font.weight,
    status: font.status
  }))
);
console.table(faces);

For a targeted assertion, ask the browser whether a concrete CSS face can be used:

const loaded = await page.evaluate(() => document.fonts.check('16px "Inter"'));
if (!loaded) throw new Error('Inter is not available for the target text');

check() is evidence about that face, not proof that every weight or glyph used by your page is present. Check each required family, weight and style when the design depends on them.

Trace a remote font request

If the face is declared but not loaded, inspect the CSS and font-file requests. Attach request and response logging around the page load:

page.on('requestfailed', request => {
  if (request.resourceType() === 'font' || request.url().match(/.woff2?(?|$)/i)) {
    console.error('Font request failed:', request.url(), request.failure());
  }
});

page.on('response', response => {
  if (response.request().resourceType() === 'font') {
    console.log('Font response:', response.status(), response.url());
  }
});

Confirm that the production URL in @font-face is correct, the file returns a successful response, and the deployed policy permits the request. Check redirects, authentication requirements and the response’s content rather than assuming a CDN, CORS rule or CSP directive is at fault. No site-specific trace is available here, so those are investigation branches, not established causes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common CSS mistakes to verify

  • The production stylesheet points at a development host or an incorrect relative path.
  • The requested weight or style has no matching src, so the browser synthesizes or falls back to another face.
  • The font is declared with font-display: optional and the capture occurs before it becomes available.
  • Application code replaces the text or font-family after your initial readiness wait; wait again after that update.

Separate launch failures from page failures

If Chromium itself fails to launch, collect Playwright’s browser diagnostics:

DEBUG=pw:browser npx playwright test

Microsoft’s Playwright CI documentation specifically recommends DEBUG=pw:browser when debugging “Error: Failed to launch browser” errors. This output helps reveal missing libraries, an unusable executable or an incompatible installation. It does not diagnose a web-font request that fails after the page has loaded; use FontFaceSet and network evidence for that case.

Compare deployment strategies

Strategy Strength Risk or trade-off When to choose
Self-managed Linux runtime Full control over base image, fonts and policies You must maintain browser binaries, libraries and fonts You already operate a pinned image and need custom system packages.
Official Playwright Docker image Browsers and system dependencies are supplied together Your application still needs its own Playwright package and any non-default fonts You want a documented starting point for CI or containers.
System-installed fonts Local rendering without a network request Every runtime image must contain identical font files and configuration The typeface is licensed and deployed as part of the image.
Remote web fonts One CSS/font deployment can serve many runtimes URL, policy, availability and timing can affect captures You need browser-delivered fonts and can monitor the asset path.

Pin both the container image and the Playwright dependency where reproducibility matters. When a local/production difference appears, compare those pins before changing screenshot thresholds.

Systematic troubleshooting checklist

  1. Classify the symptom. Decide whether the browser failed to launch, the page used fallback typography, glyphs were missing, or the capture was simply early.
  2. Record versions and environment. Save OS/base image, container status, Playwright version, Chromium version and font source.
  3. Install the matching browser and dependencies. Run npx playwright install --with-deps chromium in the executing environment.
  4. Wait after the final render state. Call await page.evaluate(() => document.fonts.ready) after dynamic content and font declarations are present.
  5. Inspect the expected face. Review document.fonts, its statuses and a targeted document.fonts.check().
  6. Inspect requests. Find failed font requests, incorrect URLs, unexpected redirects and non-success responses.
  7. Collect launch logs when applicable. Use DEBUG=pw:browser only for browser-start diagnostics.
  8. Reproduce with the production image. A result from a different operating system does not prove the production runtime is configured correctly.

Platform-specific Fedora cache reports

A Chromium issue filed July 6, 2026 reports font-cache corruption symptoms in Fedora-based systems. It is a platform-specific report, not evidence that Chromium font failures generally come from cache corruption. Consider that branch only when the runtime is Fedora-based and the observed behavior matches the report; otherwise continue with dependency, face, request and timing checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and reliability considerations

Waiting for document.fonts.ready adds only the time needed for used fonts and their layout work, but a remote asset can still delay that point. Keep font files cacheable, avoid unnecessary families and weights, and wait after the smallest state change that produces the content under test. Do not replace a deterministic readiness check with an arbitrary sleep: a fixed delay can be too short on a cold production run and waste time on a warm one.

For visual tests, keep the OS, container image, browser version and font files stable. If a failure is intermittent, log the face statuses and request outcomes with the screenshot artifact so you can distinguish a real rendering change from a transient asset failure.

Or skip the browser setup

ScreenshotNeo provides a one-request screenshot API when you do not want to maintain a Playwright runtime. Before capture it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. It also offers an MCP server for AI clients such as Claude and Cursor, with take_screenshot, get_page_info and capture_pdf tools.

Use the API documentation at https://screenshotneo.com/docs/ for parameter details. A one-call example:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Equivalent Python:

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)

Equivalent Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing gives two months free. Sign up free to try it.

Frequently Asked Questions

Does document.fonts.ready load every font declared in CSS?

No. It covers loading and layout for used fonts. An unused or optional face can remain unloaded, so inspect the specific family, weight and style your page renders.

Should I add a long timeout instead of waiting for fonts?

No. Wait for the final render state and then use document.fonts.ready; arbitrary sleeps are either flaky or unnecessarily slow.

Can DEBUG=pw:browser show why a web font request failed?

It is intended for Chromium launch diagnostics. Use FontFaceSet status checks and request/response logging for page-level font failures.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.