How to Fix Font Rendering Differences in BackstopJS Screenshots
Make BackstopJS font comparisons more consistent by matching the rendering environment, checking font availability and load timing, and tuning diffs last.
Short answer: create reference and test screenshots with the same rendering environment. Run both in the same pinned BackstopJS Docker image and browser version, make sure the intended fonts exist in that environment and have loaded before capture, and keep viewport and page state consistent. Docker can reduce differences between machines; it cannot supply a missing font or guarantee identical rasterization in every setup.
BackstopJS’s Docker guidance explains that environments can render the same page differently, especially text, and recommends rendering in a BackstopJS Docker container to reduce those differences. BackstopJS project documentation
1. Make reference and test use the same renderer
First determine whether the mismatch comes from different rendering environments or from the page itself. If the reference was captured on a developer’s machine and the test on CI, compare the operating system, BackstopJS version, browser/runtime, installed fonts, viewport, device scale, URL state, and scenario setup. A different font fallback can change text width, line wrapping, and element positions across the whole page.
The most direct approach is to run both BackstopJS commands using the same Docker configuration. From a project where BackstopJS is installed and configured:
# Capture or update references in Docker
npx backstop reference --docker
# Run visual tests in Docker
npx backstop test --docker
Use the equivalent command for the way BackstopJS is installed in your project. Check the README for your installed version: CLI flags and configuration details can vary between releases. Docker mode is documented by the project as a way to reduce environment differences, particularly text rendering.
Keep the container reproducible
- Use the same pinned BackstopJS image and browser setup for reference generation and CI tests.
- Do not silently switch the image or browser version between updating references and running tests.
- Make the container available to developers and CI so a failing comparison can be reproduced locally.
- Record viewport dimensions, device scale, scenario settings, and any environment variables that affect the page.
The BackstopJS Docker Hub listing describes a self-contained BackstopJS 3.x image with headless Chrome, but that listing is old. Treat it as a historical description, not a recommendation to use an unpinned latest image. Choose and verify the image and browser versions used by your project.
2. Check which font the browser actually uses
A CSS declaration is not proof that the requested font rendered. Inspect the affected element in the browser used for capture and check its computed font-family, then verify that the intended webfont loaded successfully. If the font is missing, blocked, or still loading when capture begins, the browser may use a fallback.
Check all font sources relevant to the page:
- System fonts installed in the container or operating system.
- Webfonts fetched from the application or a third-party host.
- Font files copied into the build or mounted at runtime.
- Network access and authentication needed to retrieve those files in CI.
- CSS rules that select a different family, weight, or style at the tested viewport.
A historical BackstopJS issue reports a local-versus-Docker font-family mismatch and describes installing the font in the Dockerfile as a workaround. That report illustrates a possible cause; exact installation steps depend on the font, base image, and license. BackstopJS issue reports
Install or copy a missing font
If the renderer needs a local font, add the appropriately licensed font to the image or install it through the base image’s supported package mechanism. Rebuild the image, then verify the font is available in that rebuilt environment. There is no single package command that applies to every font and Linux base image, so use the font vendor’s distribution instructions and your base image’s package manager.
3. Align capture settings and page state
Even with matching fonts, comparisons can differ if the browser captures a different page state. Keep these inputs aligned between reference and test runs:
- BackstopJS and browser: use the same pinned versions and execution mode.
- Viewport and scale: match screenshot dimensions and the device scale configuration supported by your installed release.
- Scenario: use the same URL, cookies, authentication, interactions, and data state.
- Responsive behavior: confirm the same media queries and font rules apply at capture time.
- Timing: capture after the page and intended fonts are ready. A fixed delay can help diagnose a race, but may make runs slower and is not a universal readiness guarantee.
BackstopJS uses scenarios to define captures and exposes browser engine options. Consult documentation matching your installed version before adding a particular font-readiness hook; the available sources do not establish one version-independent setting for waiting on fonts.
4. Tune image comparison only after rendering is understood
BackstopJS comparison settings control how image differences are evaluated or displayed. They do not install a font, make the browser choose the intended font, or make two operating systems rasterize glyphs identically.
| Setting or approach | What it helps with | What it does not fix |
|---|---|---|
misMatchThreshold |
Sets the tolerated mismatch level for a comparison. | A missing font, font fallback, or inconsistent renderer. |
requireSameDimensions |
Controls whether differing image dimensions are accepted. | Different glyph shapes or line wrapping. |
| Resemble.js diff options | Changes comparison or diff presentation behavior. | The page’s actual font selection and rendering. |
These option names and defaults should be checked against the README for the version installed in your project. The retrieved documentation describes a default misMatchThreshold of 0.1, but defaults can change. Raise tolerance only when the remaining variation is understood and acceptable; broad tolerance can conceal real regressions. BackstopJS configuration documentation
5. Troubleshooting common font mismatches
| Symptom | Likely cause | What to do |
|---|---|---|
| Text looks different only in CI or Docker | Different operating system, browser, installed font set, or BackstopJS image. | Run reference and test in the same pinned container and compare the renderer versions. |
| Text width or line breaks change substantially | The requested font or weight did not load, so a fallback rendered. | Inspect computed styles and font loading; make the font available to the capture environment. |
| The first run differs but a later run passes | Capture may happen before page assets, including fonts, are ready. | Check the page state and network behavior at capture time. Add a version-appropriate wait strategy after confirming support in your installed BackstopJS/browser setup. |
| Installing a font did not change the screenshot | The page may use a webfont, another family or weight, or a different container than the one rebuilt. | Verify the actual computed family and loaded font in the exact runtime that captures the screenshot. |
| Diffs remain as faint outlines around letters | Small rasterization or antialiasing differences may remain even when the font is correct. | Confirm environment parity first; then decide whether a limited comparison tolerance is appropriate. |
| Docker capture cannot reach the app | The app hostname or network route may not resolve from inside the container. | Use a hostname reachable from the container. BackstopJS documentation notes that some Mac and Windows setups use host.docker.internal; follow guidance for your host and version. |
| Chrome exits or refuses to launch in an older container setup | Container sandbox or launch requirements differ. | Check the BackstopJS Docker guidance for the image and host in use; some older configurations require --no-sandbox. |
BackstopJS notes that browser console logs do not appear in screenshots. A clean screenshot therefore does not prove that a font loaded successfully; inspect the rendered page and browser/network diagnostics as part of investigating the mismatch.
6. Choose Docker or native execution
| Approach | Useful when | Trade-offs |
|---|---|---|
| Shared Docker image | Local and CI machines differ, and you want a repeatable browser and font environment. | Requires maintaining the image and ensuring the container can reach the app and required font sources. |
| Native execution | Machines can be provisioned with matching operating systems, browser versions, and fonts. | Keeping every developer and CI machine aligned may take ongoing setup. |
BackstopJS documents Docker as a way to reduce cross-environment differences, but it does not establish that Docker is simpler for every team. Pick the setup your team can pin and reproduce, and generate both references and tests with that same setup.
Or skip the browser setup
For a one-off screenshot or a capture outside your visual regression suite, ScreenshotNeo returns a screenshot or PDF from one GET request. See the ScreenshotNeo API documentation.
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}`);
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed. Response headers identify the page verdict and billing status.
- An MCP server lets AI agents use screenshot, page information, and PDF capture tools.
- The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Does Docker guarantee identical text rendering?
No. It helps standardize the renderer and its environment. The correct font must still be available and loaded, and remaining differences may depend on the rendering stack.
Should I update all existing reference screenshots after fixing fonts?
Update references only after confirming the new capture environment and page output are the intended baseline. Otherwise, the reference set may simply encode an unresolved fallback or environment mismatch.
Can I fix font differences by raising the mismatch threshold?
That changes what the comparison accepts; it does not change the font or rendering. Diagnose the environment and font first, then use a carefully chosen tolerance only for known acceptable residual differences.


