ScreenshotNeo

BlogHow-to

Chrome Headless Screenshot of a Website with Indian Language Fonts Looks Wrong: Fix

Fix garbled or missing Indic text in Chrome Headless screenshots by checking script coverage, web fonts, fallback behavior, and Chrome mode.

By the ScreenshotNeo team4 October 20268 min read

If Indian-language text looks garbled, shows boxes, or disappears in a Chrome Headless screenshot, first check whether the Linux environment running Chrome has fonts with glyph coverage for the exact script. Then verify that the page’s intended web font loaded and includes those glyphs. A CSS font-family declaration alone does not guarantee either. Also compare the Chrome version and Headless mode if visible Chrome renders the same page correctly.

“Indian languages” do not all use one script or one interchangeable font. Record the language and script before choosing a font or package. There is no universal install command: the right family and package depend on the script and the Linux distribution or container image.

1. Record the capture environment

Before changing anything, collect these details so you can reproduce the result:

  • The affected language and script, and a sample of the characters that render incorrectly.
  • The page URL and whether visible Chrome on the same machine shows the defect.
  • The operating system and exact container or CI image where Chrome runs.
  • The Chrome or Chromium version, Headless mode, and capture library or command.
  • The page’s computed font-family and whether its intended web font loaded successfully.
  • The viewport, device scale factor, and any page wait or timeout settings.

Compare captures with the same URL, viewport, browser version, and font environment. Otherwise, a difference in layout, timing, or browser build can obscure the font problem.

2. Check system font coverage where Chrome runs

Check the actual runtime host, container, or CI image. A developer workstation may have fonts that are absent from the image launching headless Chrome. If the page falls back to system fonts, that environment needs a font with glyph coverage for the affected script.

Choose fonts based on the script and verify their coverage; do not assume that a font for one Indian language covers another. Package names vary by distribution and can change. A 2019 Ubuntu support discussion mentions fonts-indic and fonts-noto as possible leads, but this is not a universal or current install recipe. Check your target distribution’s package repository and documentation before using a package name. CJK fonts address Chinese, Japanese, and Korean coverage and are not a fix for missing Indic glyphs.

After adding or updating fonts, restart Chrome so it sees the changed environment, then capture again. If you build a container image, include the chosen fonts in the image used for the capture rather than installing them only on a separate host.

3. Verify the website’s web font and CSS

Inspect the affected text in the browser runtime. Check the computed font-family, the network request and loading status for each intended web font, and whether the loaded font actually contains the required script glyphs. A declared family can fail to load, or load successfully but lack the characters on the page. In either case, the browser may use fallback fonts.

For multilingual pages, use UTF-8, suitable web fonts with coverage for the scripts you display, and accurate lang attributes on the document and language-specific content. Locale-aware CSS can help select typography intentionally. Embedded web fonts can make appearance more consistent across platforms, but they still need the right glyph coverage, successful loading, and a license that permits your use and distribution. The India Universal Acceptance roadmap recommends embedded fonts for uniform display and calls out UTF-8, lang attributes, locale-aware CSS, and cross-platform differences.

System fonts and bundled web fonts have different tradeoffs:

Approach Check Tradeoff
System-installed fonts Coverage in the exact host or image running Chrome Can use platform fonts, but output depends on what the environment installs and updates.
Bundled or embedded web fonts Successful font loading and glyph coverage for every required script Can improve consistency across platforms, but adds asset management and licensing considerations.

4. Check locale only when relevant

On Linux, Chromium follows the system locale. Environment variables including LANGUAGE, LC_ALL, and LC_MESSAGE can override LANG. Check locale if language-sensitive behavior is unexpected, but changing locale does not install a missing font or add glyphs to a font that lacks them.

5. Compare Chrome Headless modes and versions

If visible Chrome renders correctly but your screenshot does not, record the version and mode for both. New Headless shares the regular Chrome implementation; old Headless used a separate implementation and could differ. Chrome’s Headless documentation says new Headless has been available since Chrome 112. Chromium’s Headless README says old Headless functionality is no longer part of the Chrome binary as of M132 and directs users who still need it to chrome-headless-shell.

Use the current Headless mode for a comparison with regular Chrome. If your workflow intentionally depends on old Headless, consult the Chromium documentation for chrome-headless-shell and account for its separate distribution. Avoid attributing every font discrepancy to Headless: missing coverage, failed web-font loads, and platform fallback are separate causes to check first.

6. Recapture with a controlled command

Once the font environment or page font configuration is corrected, recapture with a fixed URL, viewport, Chrome version, and mode. Chrome’s official example uses --headless=new, --screenshot, and a window size. Add a timeout for pages that need time to load; use the smallest wait that reliably allows the required fonts and content to render.

chrome --headless=new --screenshot --window-size=412,892 --timeout=5000 "https://example.com/page"

The example writes a screenshot in the current directory. Replace the URL with your page. The timeout is an example value, not a guarantee that every page will finish loading in five seconds. Confirm that the screenshot contains the expected script, then repeat the capture to check that the result is stable.

Troubleshooting

Symptom Likely cause What to check or change
Boxes, empty spaces, or missing characters No available font has glyph coverage for the affected script. Check installed font coverage in the exact runtime image; install a suitable font from that distribution’s repository and restart Chrome.
Some characters render while others do not The selected or fallback font has partial coverage, or the page mixes scripts. Identify the exact script and affected code points; verify that a font used for each text run covers them.
Text looks different from the developer workstation The workstation and capture image have different fonts, fallback behavior, or browser versions. Compare the OS image, installed font families, Chrome version, and computed font stack. Make the capture environment deliberate.
The CSS names the correct web font, but output is wrong The font request failed, the font loaded too late, or the file lacks the needed glyphs. Inspect the request status and loaded font, wait for the font before capture, and verify coverage rather than relying on the CSS declaration.
Headless differs from visible Chrome The modes or versions differ, or the environments do not share the same fonts. Compare the exact Chrome version and mode, OS image, URL, viewport, and font installation. Check whether the workflow uses old Headless.
Changing LANG does not fix missing text Locale affects language-sensitive behavior; it does not supply glyphs. Verify font coverage first. Inspect LANGUAGE, LC_ALL, and LC_MESSAGE only if locale behavior is also relevant.
Screenshot captures before the page font appears The page or font request has not completed when capture starts. Wait for the relevant content or font readiness using your capture tool’s supported wait mechanism, or adjust the Chrome timeout where applicable.

Performance, reliability, and cost considerations

  • Image size and maintenance: Adding fonts increases the runtime image’s contents and creates an update and license-management responsibility. Include only the coverage your pages require, while accounting for every script present.
  • Capture consistency: Pin or record the browser version and image, and keep the viewport and capture timing consistent. Font changes can affect glyph shape, line breaks, and page layout.
  • Loading time: Web fonts and page content may need time to load. A longer fixed delay can waste time and still miss a failed request; prefer a readiness condition supported by your capture implementation when possible.
  • Reliability: Treat a declared font as insufficient evidence. Check actual loading and coverage, then validate the output after updating fonts or browser versions.
  • Cost: The dossier identifies no reliable statistic for how often this issue occurs or the success rate of a particular fix. Cost depends on your browser runtime, image distribution, and capture workflow; do not infer a universal savings or failure rate.

Or skip the browser setup

If you need a screenshot without managing a Chrome runtime, ScreenshotNeo provides a website screenshot API and MCP server. It accepts a URL in one GET request and can return PNG, JPEG, WebP, or PDF. It is a capture service, so still verify that the rendered page uses suitable fonts for the target script.

Example using cURL:

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

See the ScreenshotNeo API documentation for configuration and response details. Python:

import requests

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

Node.js:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/page',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
  • Bot checks and CAPTCHAs, blank pages, failed loads, timeouts, and cache hits cost nothing. Responses identify the page verdict and billing status in headers.
  • An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
  • The Free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.

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

FAQ

Is there one font package that fixes every Indian language?

No. Identify the script and choose fonts with the required glyph coverage. Package names depend on the distribution.

Will setting a Hindi or other locale install missing glyphs?

No. Locale can affect language-sensitive behavior, but fonts must be present and cover the characters.

Does this prove Chrome Headless has an Indic rendering bug?

No. Missing runtime fonts, web-font loading or coverage, fallback differences, and browser mode or version are all plausible causes to investigate.

What details should I provide when asking for help?

Share the script and sample characters, OS or image, Chrome version and mode, capture method, font stack and load status, and whether visible Chrome shows the same issue.

Sources