How to Fix Emojis Not Rendering in Headless Chrome Screenshots
Fix missing, monochrome, or boxed emojis in headless Chrome by checking Linux fonts, fontconfig fallback, and the exact browser environment.

When emojis are missing from a headless Chrome screenshot on Linux, the usual cause is that the environment running Chrome lacks an emoji-capable font or does not select it through font fallback. A screenshot captures the page Chrome rendered; the screenshot flag does not install fonts or supply missing glyphs. Install and verify an emoji font in the same container or VM that launches Chrome, restart the browser, then test the exact emoji sequence there.
This guide focuses on Linux, where the available evidence points to system fonts and fontconfig fallback as the main troubleshooting path. macOS, Windows, and different Linux images have different font stacks, so do not assume a fix for one environment applies to another.
1. Identify the environment that actually renders the page
Start with the machine that produces the image, not the machine where you inspect it. A local Chrome window may have fonts installed that are absent from a CI runner, server, Docker image, or remote browser. Record the operating system and distribution, Chrome or Chromium version, container image, and how the capture process starts the browser.
Chrome’s documented command-line screenshot option captures a rendered page, and its example sets a viewport with --window-size. Neither option changes the system’s font inventory. [Chrome for Developers: Headless mode]
google-chrome --headless --window-size=800,400 --screenshot=/tmp/emoji.png https://example.com/emoji-test.html
For Chromium installations, the executable may instead be named chromium or chromium-browser. Use the binary and launch flags already used by your capture job so your test represents the actual runtime. If screenshots are produced in a container, run all the checks and package changes inside that container image.
2. Check for an emoji font and install one when needed
Linux emoji rendering depends on installed fonts and fallback selection. Chromium’s historical Linux work documented cases where Chrome chose outline or contour fonts for some emoji even when a dedicated color emoji font was present. That history is useful context, but it is not a guarantee that every current Chrome build uses the same fallback behavior. [Chromium change 671511b0]

On Ubuntu, a community support thread reported that installing fonts-noto resolved missing characters in headless Chrome. It also mentioned fonts-indic and fonts-noto-cjk for broader script coverage. Treat that as a user-reported workaround, not current official package guidance and not proof that all three packages are needed for emoji. Check package names and availability for your distribution and image version. [Ubuntu community thread]
Ubuntu or Debian example
For a Debian-family image, install the package available in that distribution’s repositories, then refresh fontconfig’s cache if the image tooling does not do so automatically. Package names and repository contents can change, so confirm them for the base image you use.
apt-get update
apt-get install -y fonts-noto
fc-cache -f -v
If you also need Indic or CJK scripts, select the corresponding packages for the distribution rather than installing them as an emoji-specific requirement. Keep the package installation in the image build so each deployment receives the same font set.
Inspect fonts and fallback configuration
The fc-list and fc-match commands can help inspect fonts known to fontconfig and the font selected for a family request. Exact output depends on installed packages and configuration. These are diagnostics, not proof that every emoji sequence will render in color.
# List fonts known to fontconfig; filter output for likely emoji font names.
fc-list : family | sort -u | grep -i emoji
# Ask fontconfig which font matches an emoji family request.
fc-match emoji
# Check the fontconfig cache and configuration paths available in this image.
fc-cache -v
If these commands are unavailable, install the fontconfig utilities package for your distribution or inspect fonts through the image’s package and font directories. Avoid copying a fontconfig snippet from another system without understanding its effects.
3. Test the exact emoji in the capture environment
Create a small page with the exact text that fails, including variation selectors, skin-tone modifiers, flags, or joined sequences. Emoji can be composed of multiple code points; the sources do not quantify support for each sequence, so test the specific characters your application uses.

<!doctype html>
<meta charset="utf-8">
<style>
body { font: 48px sans-serif; padding: 24px; }
</style>
<p>Basic: 😀 ❤️ 👍🏽</p>
<p>Flag and joined sequence: 🇺🇳 👩💻</p>
Save this as emoji-test.html and open it using the same browser binary, runtime user, and container as the screenshot job. Compare the screenshot with how the same page renders in a headed browser launched from that environment, if one is available. This comparison is a practical diagnostic suggestion: the research sources do not provide a universal debugging command or claim that headed and headless rendering always behave identically.
- Capture the test page before changing the image, so you have a baseline.
- Install or correct the font in the runner, then refresh the font cache if appropriate.
- Stop and restart the Chrome process. A running process may not pick up changes made after it started.
- Capture the same page and exact sequence again, with the same viewport and browser flags.
- Check whether the result is a missing-glyph box, a monochrome symbol, or a correctly rendered color emoji; those symptoms can point to different font selection outcomes.
4. Understand font fallback without overfitting the fix
Chromium’s documented implementation history says Linux fontconfig can prefer color emoji fonts through the und-Zsye locale or the special emoji family. In the cited change, Chromium also changed its Linux fallback locale to und-Zsye. This describes a particular implementation change; it is not an instruction to hard-code that locale in every current deployment. Verify behavior with the Chrome build and fontconfig configuration you ship. [Chromium change 671511b0]
Do not blindly prepend an emoji font to every system font. The same Chromium change notes that broad fallback workarounds can affect ordinary characters as well as emoji. Prefer installing a suitable font and confirming what fontconfig selects for the relevant family and text. If the font is installed but the screenshot remains wrong, inspect the actual selected font and the runner’s fontconfig configuration before adding overrides.
Platform-specific font behavior also matters. A separate Chromium change recorded defaults for a particular historical -webkit-pictograph feature across macOS, Windows, Linux, and ChromeOS. Those implementation details are not a current, universal mapping of each operating system’s emoji font stack. Use platform-specific documentation and direct checks for non-Linux environments. [Chromium change 7c6e78c]
5. Make the fix reproducible in CI and containers
A font fix that exists only on one runner is fragile. Put required font packages and any deliberate fontconfig configuration in the same image build or provisioning steps that install Chrome. Pin the base image and browser versions according to your project’s release process, and rebuild the image when those inputs change.
- Use the production runtime: match the service account, container, filesystem, and browser binary that create real screenshots.
- Keep package installation explicit: make the image declare which font packages it needs.
- Refresh caches as needed: run fontconfig cache refresh after installing fonts when required by the distribution.
- Restart browser workers: recycle persistent browser processes after changing system fonts.
- Keep a minimal regression page: include representative basic emoji and the multi-code-point sequences your product depends on.
- Compare environments intentionally: if local and CI output differs, compare OS image, Chrome build, font package set, and fallback configuration.
Do not infer that a browser upgrade alone will supply fonts missing from a Linux image. Likewise, installing a font does not guarantee that every sequence will render identically across fonts, browser versions, and platforms. Verify the output you need in the environment that matters.
6. Troubleshooting common symptoms
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Emoji appears as an empty square or replacement glyph | No installed font covers the glyph, or fallback does not select an appropriate font. | Check the runner’s font inventory and fontconfig match. Install an emoji-capable font package for the distribution, refresh the cache if needed, restart Chrome, and recapture. |
| Emoji is monochrome instead of color | A fallback font may cover the character but not provide the expected color presentation, or a different font was selected. | Inspect the selected font and fallback configuration. Test the same code points in a minimal page. Do not assume the screenshot format itself can add color glyph support. |
| Only some emoji fail | The failing symbols may use different code points or composed sequences, and font coverage can differ. | Test the exact characters, including variation selectors and joiners. Confirm coverage and fallback for those characters instead of testing only a basic smiley. |
| It works locally but fails in CI | The local desktop and runner have different operating systems, font packages, browser versions, or fontconfig configuration. | Run font and browser checks inside the CI image. Add the needed fonts to that image and compare the same test page and browser build. |
| Installing a font did not change an existing worker | The browser process may still be using the font state available when it started, or fontconfig cache/configuration may not reflect the installation. | Refresh the cache where appropriate, inspect fontconfig output, terminate and relaunch the browser worker, then capture again. |
| Emoji fix changes ordinary text rendering | A broad font fallback override may affect more than emoji. | Remove the global override and inspect targeted family or locale selection. Chromium’s historical change warns that broad workaround patterns can affect ordinary characters. |
| Font commands return no emoji entry | The image may not contain fontconfig utilities, the package may be missing, or the expected family name may differ. | Install diagnostic utilities for the distribution or inspect its installed font packages and files. Verify package naming against the image’s repositories. |
7. Performance, reliability, and cost considerations
Font installation affects the image build and runtime environment, while screenshot rendering still depends on loading and painting the page. The research does not provide benchmark data for font package size, rendering speed, or emoji success rates, so measure those in your own pipeline if they affect capacity planning. Keep the font set as small as your script coverage needs and avoid repeatedly installing packages at job startup when they can be included in a reusable image.
For reliability, treat fonts as part of the rendering dependency set alongside the browser version and operating system image. A deterministic container with explicit font packages makes differences easier to diagnose. When a screenshot is missing glyphs, retrying the same capture without changing the font environment is unlikely to address the underlying cause.
For cost, there is no paid physical product required by the documented remedies. Linux font packages are installed through the distribution’s package management; check the terms and repository availability of the distribution you use. Avoid attributing monetary savings or speed improvements to a font fix without measurements from your own deployment.
8. Or skip the browser setup
If you need screenshots without maintaining a browser and font environment, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API returns PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options and formats.
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, timeouts, failed loads, and cache hits cost nothing, with response headers indicating the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. The API includes options such as full-page capture with lazy images loaded, element capture, device presets, custom CSS and JavaScript, wait conditions, request blocking, and caching. These capabilities remove browser setup from your application, but they do not replace checking that the target page itself renders the emoji you expect.
Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card required.
9. Frequently asked questions
Does --screenshot install or bundle emoji fonts?
No. It captures what Chrome renders in its environment. The font packages and fallback behavior come from the operating system and browser environment.
Is fonts-noto guaranteed to fix every emoji?
No. It was named in a community report about missing characters on Ubuntu Server. Confirm the package’s current contents and test the exact emoji sequences in your own image.
Should I set the fallback locale to und-Zsye?
Not as a universal copy-and-paste fix. Chromium used that locale in a documented Linux implementation change, but the right behavior depends on the Chrome build and fontconfig setup you deploy.
Why does one emoji render while a flag or joined emoji does not?
Some displayed emoji are sequences of code points rather than one character. Test the exact sequence and inspect font coverage and fallback in the target environment; the available research does not establish universal sequence coverage.
Will changing screenshot dimensions fix missing emoji?
Usually not. A viewport setting changes the capture dimensions, not the fonts available to Chrome. Diagnose the font environment first.


