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

Most wkhtmltoimage rendering failures have one of four causes: the input bytes are not actually UTF-8, the document or HTTP response declares the wrong charset, the required glyphs are missing from installed fonts, or Qt WebKit cannot implement the HTML5/CSS feature you used. The --encoding utf-8 switch only supplies a default for input; it does not repair invalid bytes, install fonts, or turn wkhtmltoimage into a modern browser.

Work through the checks below in order. First identify the exact binary and build, then separate decoding problems from font problems, and finally reduce modern HTML/CSS issues to a small test case. The commands are written for Ubuntu, but package behavior can vary by release and architecture.

1. Identify the binary, version, and build

Before changing HTML or installing fonts, verify what executable is really being called. Ubuntu packages, upstream patched-Qt packages, containers, wrappers, and application services may invoke different binaries.

command -v wkhtmltoimage
wkhtmltoimage --version
wkhtmltoimage --extended-help
  • command -v shows the executable found in your current PATH.
  • --version records the release and may indicate whether a patched Qt build is present.
  • --extended-help confirms which options that particular binary exposes.

If a web application, queue worker, systemd service, or Docker image launches wkhtmltoimage, inspect that environment too. A service can have a different PATH, home directory, locale, font directories, and binary than your interactive shell.

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

The Ubuntu Jammy manual documents package version 0.12.6-2; the upstream project identifies 0.12.6 as its stable line, released June 11, 2020. These facts do not establish identical support on every Ubuntu release. Record the Ubuntu version, CPU architecture, package source, and full version output before applying build-specific advice.

2. Decide whether the symptom is decoding or font coverage

Garbled text, replacement characters, or question marks

Mojibake such as Chinese text turning into unrelated Latin characters, or characters becoming replacement symbols, usually indicates that bytes were decoded with the wrong charset or that the source bytes are malformed. Check the file itself rather than trusting an editor tab label.

file -bi page.html
locale

file -bi is a useful hint, not a complete validator. Open the file with a UTF-8-aware editor or inspect it with a script that reads bytes explicitly. Confirm that the producer wrote Unicode text encoded as UTF-8 and that no later conversion changed those bytes.

Empty squares, missing symbols, or text that disappears

Correct decoding does not guarantee visible text. A renderer still needs a font containing every required glyph. CJK characters, emoji, mathematical symbols, and many historic scripts are common examples of characters absent from a minimal server installation. Upstream packaging documentation notes runtime dependence on installed fonts, fontconfig, and freetype2.

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

Test with a font family known to be installed, then install an appropriate language font package for your Ubuntu release if necessary. After installation, refresh fontconfig’s cache and restart the process that performs the capture:

fc-cache -f -v
fc-match sans-serif

The exact package name depends on the script and Ubuntu release. Verify the package in your release’s repositories instead of copying a package name intended for another distribution. If fc-match returns an unexpected fallback, your CSS family may not be available to the service account.

3. Declare UTF-8 in the HTML and preserve the bytes

For a local document, put a charset declaration near the beginning of <head>, before substantial text:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>UTF-8 test</title>
</head>
<body>
  <p>English — 中文 — العربية — हिन्दी — 日本語 — € — ✓</p>
</body>
</html>

Save the file as UTF-8, preferably without an editor silently converting it to a legacy code page. The HTML content-type meta form is also valid, but do not place conflicting declarations in the same document.

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.

For an HTTP URL, inspect the response’s Content-Type header as well as the markup. A response might declare a charset that disagrees with the document. An archived upstream issue describes garbled Chinese text even when UTF-8 options and a meta declaration were present; a maintainer raised a possible header/markup interaction as a diagnostic suspicion, not a universal precedence rule. Treat the header as evidence to check, not as proof of one fixed ordering.

When you control the server, send a consistent header such as Content-Type: text/html; charset=utf-8 and keep the HTML declaration consistent. When you do not control it, save the response and compare its bytes, header, and markup in a minimal reproduction.

4. Use --encoding for its actual purpose

The Ubuntu wkhtmltoimage manual describes --encoding <encoding> as setting the default text encoding for input. Use it when the input lacks usable encoding information:

wkhtmltoimage --encoding utf-8 input.html output.png

This option does not:

  • convert incorrectly encoded bytes into the intended Unicode characters;
  • override every contradictory HTTP or document declaration;
  • install a font or add missing glyphs;
  • fix invalid HTML; or
  • upgrade Qt WebKit’s HTML5 and CSS support.

The upstream 0.12.6 changelog records a change allowing --encoding to work for non-patched builds. That is a version/build-specific changelog note, not a guarantee that every Ubuntu package treats malformed input identically. If adding the switch changes the result, capture both commands and inspect the source bytes rather than assuming the switch repaired the file.

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

5. Capture local files safely

A reproducible local test removes network headers and application code from the diagnosis:

  1. Create a tiny HTML file containing a known UTF-8 test string and the explicit meta declaration.
  2. Confirm the file is readable by the user running wkhtmltoimage.
  3. Run the command with and without --encoding utf-8.
  4. Compare the output using a font you know exists and then with the target font.
  5. Only after that, add your production CSS, JavaScript, images, and external resources one at a time.
wkhtmltoimage --encoding utf-8 --enable-local-file-access input.html output.png

Use --enable-local-file-access only when the document genuinely needs local resources, and restrict the files available to an isolated test directory. Do not enable broad local access for untrusted HTML.

6. Account for Qt WebKit’s HTML5 and CSS limits

wkhtmltoimage renders through Qt WebKit, not a current Chromium, Firefox, or Safari engine. Modern HTML and CSS can therefore behave differently even when the source is valid and UTF-8 is perfect. Typical warning signs include layout changes, unsupported selectors, missing flexbox or grid behavior, JavaScript APIs that never run, and lazy-loaded content that remains blank.

Do not treat every discrepancy as an Ubuntu locale problem. Reduce it to a fixture that contains one failing feature and verify the renderer version first. Then consider a compatibility rewrite:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • replace unsupported layout features with simpler block or table layout for the capture;
  • provide explicit widths, heights, and fallback fonts;
  • avoid relying on browser-only JavaScript APIs;
  • inline critical CSS and assets while diagnosing;
  • wait for a deterministic element or a known delay before capture; and
  • compare the same fixture in a current browser to identify engine differences.

A command-line flag cannot make arbitrary contemporary HTML5 pages render as though they were processed by a current browser.

7. Choose an Ubuntu package or build carefully

Distribution builds and upstream patched-Qt builds can differ in behavior and available features. The upstream project explains that some distributions compile without its Qt patches; static builds also continue to depend on system libraries, fontconfig, freetype2, and other runtime components.

  • Prefer a package intended for your Ubuntu release and architecture.
  • Record whether it is a distribution build or an upstream patched build.
  • Check runtime dependencies inside the same container, service account, or host that performs captures.
  • Do not assume a Jammy manual page proves support for a newer or older Ubuntu release.
  • Retest after replacing the binary because a font or CSS workaround may have been compensating for a build difference.

There is no single universally best build. The correct choice depends on the Ubuntu release, architecture, required HTML/CSS features, available libraries, and the fonts your application needs.

8. A repeatable diagnostic checklist

  1. Identify: run command -v, --version, and --extended-help.
  2. Isolate: reproduce with a tiny local HTML fixture.
  3. Validate bytes: confirm the producer and file are really UTF-8.
  4. Align declarations: use <meta charset="utf-8"> and, for HTTP, a matching Content-Type header.
  5. Try the default: add --encoding utf-8 only when input encoding is otherwise unspecified.
  6. Check glyphs: use fc-match, install the required font package, and rebuild the font cache.
  7. Check engine limits: remove or rewrite unsupported HTML5/CSS features.
  8. Check environment: repeat under the service’s user, working directory, locale, and binary path.

9. Troubleshooting common failures

“I used --encoding utf-8, but text is still garbled.”

Check the original bytes and the HTTP header. The switch is only a default; it cannot decode bytes that were saved in another encoding or repair corruption introduced upstream.

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

“I see boxes instead of characters.”

This is usually font coverage, not UTF-8 decoding. Confirm the font is installed and discoverable by the same account that runs wkhtmltoimage. Add a suitable fallback family and refresh fontconfig.

“The command works in my shell but not in the web application.”

Compare executable path, version, environment variables, home directory, permissions, working directory, network access, and font configuration. Services frequently run with a smaller PATH and a different font view.

“The page is correct in Chrome but broken in the image.”

First prove the encoding and fonts with a minimal fixture. If those pass, the remaining difference is likely Qt WebKit compatibility, timing, resource loading, or a build-specific patch set. Simplify the CSS and provide explicit fallbacks.

“A remote page has different characters from the saved HTML file.”

Compare the response’s Content-Type header, redirects, compression/decompression path, and final response body. A saved copy may have been decoded or rewritten by a tool before wkhtmltoimage reads it.

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

“An upgrade changed the output.”

Keep the old and new --version output, package provenance, Ubuntu release, and font inventory. Build changes can alter both WebKit behavior and font fallback, so test a fixture before changing application code.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a dependable website image rather than maintaining an old Qt WebKit environment, ScreenshotNeo provides a website screenshot API and MCP server. One request can return PNG, JPEG, WebP, or PDF, while options cover full-page capture, lazy images, CSS-selector elements, dark mode, device presets, custom viewport and retina scale, PDF paper and page ranges, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, blocked ads or requests, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.

Before capture it accepts cookie/consent banners 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 response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

Use the documented endpoint and parameters (the API also accepts parameter names commonly used by other screenshot services):

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

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)

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}`);

See the ScreenshotNeo documentation for authentication and option names. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

10. When to move away from wkhtmltoimage

Keep wkhtmltoimage when its older rendering model is acceptable, your fonts are controlled, and reproducibility matters more than current browser compatibility. Consider a browser-based capture service or current browser automation when the page depends on modern CSS, JavaScript APIs, consent cleanup, dynamic loading, or broad Unicode coverage that you cannot reliably reproduce on the Ubuntu host. Make that decision after proving the input bytes and font inventory; otherwise a renderer migration can hide a basic encoding defect rather than solve it.

Frequently Asked Questions

Does UTF-8 require a special Ubuntu locale?

A matching process locale is useful, but it does not replace valid UTF-8 bytes, a correct document or HTTP declaration, and fonts containing the requested glyphs.

Is wkhtmltoimage the same as a current browser?

No. It uses Qt WebKit, so valid modern HTML5 and CSS can still render differently from current Chromium, Firefox, or Safari.

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.

Should I install more fonts or change the encoding first?

Use the symptom: mojibake and replacement characters point first to bytes and charset declarations; empty squares point first to font coverage and fontconfig.

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.