ScreenshotNeo

BlogHow-to

How to Fix Devanagari Text Breaking in Website Screenshots from Headless Chrome

Fix broken Devanagari in headless Chrome screenshots by checking runtime fonts, font loading, fallback, and browser consistency.

By the ScreenshotNeo team4 October 20267 min read

If Devanagari text looks correct in your local browser but breaks in a headless Chrome screenshot, first check the screenshot runtime’s fonts and fallback. A Linux container can launch Chrome successfully without having the Devanagari-capable font your page expects. Confirm that the intended font is installed or that its webfont loads before capture, inspect the font actually used for the Devanagari text, and reproduce the issue in the same browser build and container used in deployment.

Why Devanagari can look different in a screenshot

Devanagari requires text shaping: the browser maps character sequences to glyphs and positions them appropriately. Chromium uses shaping and font fallback; if a preferred font lacks the required glyphs, a fallback font is selected. A different fallback can change how text appears. This is a useful diagnostic model, but it does not prove that every rendering defect has the same cause. Chromium’s RenderText overview describes shaping and fallback.

CSS can name a font that is not installed in the Linux image. A webfont can also be requested but fail or arrive after the screenshot starts. Puppeteer’s troubleshooting guidance covers Linux runtime dependencies and notes that some character sets need additional font files. A working Chrome launch therefore does not confirm that the runtime has the fonts your page needs.

Diagnose the screenshot runtime step by step

  1. Reproduce in the deployed image. Run the same page using the exact container or deployment image that creates the screenshot. Record the OS image, Chrome or Chromium version, Puppeteer version, and installed fonts. A local desktop reproduction alone can hide an environment difference.
  2. Check font availability. Confirm that the intended Devanagari-capable font is installed in that runtime. If the page uses a webfont, inspect the browser’s network activity and verify that the font request succeeds. CSS naming a family does not install it in the container.
  3. Inspect the rendered font. Check the element’s computed font-family in the affected runtime, then use the browser’s rendered-font inspection, where available, to identify the font used for the Devanagari run. The computed CSS family list is a request; the actual font can be a fallback.
  4. Wait for fonts before capture. Ensure the page’s fonts have finished loading before taking the screenshot. A capture taken while a webfont is still loading can preserve the fallback rendering. In Puppeteer, wait for document.fonts.ready after navigation and before capture.
  5. Compare like with like. Hold the page, browser build, OS image, installed fonts, and loaded assets constant when comparing local and deployed captures. Chrome documents that unified headless and headful modes share Chrome code starting with Chrome 112; the old headless implementation became a separate chrome-headless-shell from Chrome 132. See Chrome’s headless mode documentation. Historical headless workarounds may not apply to current Chrome.
  6. Try a known alternative font if needed. If the expected font is present and loaded but shaping still looks wrong, test another known Devanagari font and inspect the runtime’s fontconfig substitutions. A reported FreeSans substitution issue is one environment-specific case, not a universal diagnosis or fix. See the Chrome Help Community discussion.
  7. Install missing fonts for the actual image. Use the package manager and package name for your distribution, or bundle the needed font files into your image. There is no single responsible install command without knowing the image and distribution. Consult Puppeteer’s platform troubleshooting guide for runtime dependency guidance, then verify the font is visible in the built image.

Runnable Puppeteer example

This Node.js example waits for the page to load and for the document’s fonts to finish loading before saving a full-page PNG. It assumes Puppeteer and its browser are already installed and that the target page is reachable from the runtime.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle0' });
    await page.evaluate(() => document.fonts.ready);
    await page.screenshot({ path: 'screenshot.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Replace the example URL with the page that contains the affected text. networkidle0 waits for network activity to settle, which may not be appropriate for pages with persistent requests; in that case, use a suitable navigation condition and explicitly wait for the relevant font or selector. Waiting for document.fonts.ready cannot fix a missing font or a failed font request. It only lets fonts that the page can load finish loading.

How to confirm the font-loading diagnosis

  • Capture a page that uses a known installed Devanagari font, then compare it with the failing page.
  • Check the browser console and network requests for failed font loads, blocked cross-origin requests, or invalid font responses.
  • Compare the computed family list and the actual rendered font for the affected text in local and deployed runs.
  • Inspect the deployed image itself for the intended font files and fontconfig substitutions.
  • Change one variable at a time: font availability, browser build, or page assets. This helps separate font coverage and loading problems from other differences.

Common problems and fixes

Symptom Likely cause What to check or change
Boxes or missing glyphs No selected font has the required glyph coverage. Install or bundle a Devanagari-capable font, confirm it is available to Chrome, and capture again.
Text looks different only in the container The container selects a different fallback, or has different fonts or assets. Compare installed fonts, rendered-font inspection, fontconfig substitutions, and font requests between environments.
The first capture is broken but a later one looks right The screenshot may be taken before the webfont finishes loading. Wait for document.fonts.ready and verify the font request succeeds before capture.
Chrome launches, but Hindi still renders incorrectly Browser launch success does not establish font coverage or correct fallback. Inspect the font actually used for the affected text and install or bundle the required font in the runtime image.
Local headless and deployed headless disagree Browser versions, OS images, font sets, or loaded resources differ. Record and align those inputs before trying browser-mode workarounds.
Waiting longer changes nothing The font may be absent, blocked, invalid, or unsuitable; this is not just a timing issue. Inspect the font request and coverage. Test a known Devanagari font in the same image.
A package-install command from another guide fails Package names and available font packages depend on the Linux distribution and image. Identify the base image and use its package manager and repositories. Verify the resulting font in the image.

Performance, reliability, and cost considerations

Waiting for network idle can add latency or never complete on pages with persistent network activity. Prefer waiting for the specific font readiness or page condition you need when that is sufficient. Bundle fonts in the runtime when predictable availability matters, or serve webfonts reliably and verify their requests. For reproducible captures, keep the browser build, image, fonts, and page assets consistent. A successful process exit or screenshot file does not guarantee correct glyph coverage; inspect representative Devanagari content after changing the image or browser.

Or skip the browser setup

ScreenshotNeo is a website screenshot API: one GET request returns an image or PDF. Use it when you want to avoid maintaining your own browser capture setup. It removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed; and an MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. See the ScreenshotNeo API documentation for options and behavior.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Node.js:

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

These examples show the basic request. ScreenshotNeo supports capture options such as viewport and device presets, full-page capture, custom CSS and JavaScript, waiting conditions, custom headers and cookies, caching, and asynchronous jobs. A screenshot API does not change the fonts or assets served by the target page, so verify the returned capture when diagnosing a page-specific font issue.

Sign up for 1,000 free screenshots a month with no card.

FAQ

Does headless Chrome inherently break Devanagari?

No. Font availability, fallback, loading, and environment differences are practical first checks. Current Chrome’s unified headless mode shares Chrome code with headful mode starting with Chrome 112, though the runtime’s fonts and assets can still differ.

Will setting a Devanagari font in CSS install it in Linux?

No. The font must be available to the browser, either in the runtime or as a successfully loaded webfont.

Which Linux package should I install?

That depends on the distribution and image. Identify the base image first, then use its package sources or bundle the needed font files.

Can a screenshot service fix a broken font in the source page?

A capture service can simplify browser setup, but the page still needs to serve a usable font. Check the page’s font coverage and loading if the output remains incorrect.