ScreenshotNeo

BlogHow-to

Chromium Headless Screenshot Is Blank: Causes and Fixes

Diagnose blank Chromium headless screenshots by checking navigation, page readiness, capture settings, and graphics—in that order.

By the ScreenshotNeo team4 October 20268 min read

A blank Chromium headless screenshot usually means one of four layers failed: navigation or session control, page readiness, screenshot output settings, or graphics presentation. Check them in that order before changing browser flags. A successful navigation message does not prove the browser reached the intended page, and a longer wait cannot fix a page that never loaded.

Start by recording the browser version and binary, then verify the current URL and DOM, confirm the screenshot format and dimensions, and wait for a page-specific readiness signal. Investigate GPU settings only if the evidence points to a graphics problem.

1. Identify the Chromium binary and headless implementation

Record the exact executable, version, operating system or container, automation library and version, and whether the page uses WebGL or WebGPU. This helps distinguish an outdated command copied from a guide from a failure in the current browser or automation session.

chromium --version
which chromium

Use the actual executable name and path for your system; it may be google-chrome, chrome, or a packaged Chromium binary. If your automation library launches the browser, log the executable path configured there too. Starting a different binary manually can make a command-line comparison misleading.

There is a significant version change to account for: from Chrome milestone 132, the old Headless implementation is no longer part of the Chrome binary. Users relying on that implementation are directed to chrome-headless-shell. Advice written for older headless Chrome may not describe the binary you now run. See the Chromium Headless README.

2. Confirm navigation reached the expected page

Before interpreting the image, inspect the current URL, page title, document readiness state, and a short excerpt of the body text. If the browser is at about:blank or the document has no expected content, the problem is navigation, target selection, browser context, or session control—not screenshot rendering.

In Chrome DevTools Protocol-based automation, make sure you are inspecting the same page or target that received navigation. Check that the navigation operation is awaited, that the right browser context is active, and that the automation connection has not switched to a new blank tab.

An individual Chrome DevTools MCP issue report described a run that remained at about:blank despite a reported successful navigation, while manual headless Chrome reportedly worked. Treat that as an example of why to inspect browser state, not as evidence of a general MCP defect: Chrome DevTools MCP issue tracker.

3. Run a direct Chromium capture

A direct command-line capture provides a useful baseline. Replace the URL with a page you control and the output path with a writable location. This example requests a PNG at a 1280 by 800 viewport:

chromium --headless --no-sandbox --window-size=1280,800 --screenshot=/tmp/page.png https://example.com

--no-sandbox is commonly needed in restricted containers, but it weakens browser isolation; use it only in an appropriately isolated environment. If direct capture works but your automation library produces a blank file, focus on the automation target, session, timing, and screenshot call before changing Chromium graphics flags.

Check that the file was freshly written, has nonzero size, and has the dimensions you requested. Open the actual output path—not a cached preview or an older file with the same name. Chromium’s screenshot command handler recognizes .png, .jpeg, .jpg, and .webp; unsupported extensions can be reported by the handler. See the Chromium headless command handler.

4. Check page readiness and capture timing

A screenshot can be taken before client-side rendering, fonts, images, or other assets have finished appearing. First compare the DOM when the screenshot is taken with what you expect. Prefer an application-specific readiness condition, such as a known page heading or content container, over an arbitrary sleep.

For a quick CLI timing diagnostic, Chrome documents --timeout and --virtual-time-budget as ways to allow delayed content to appear in headless captures:

chromium --headless --no-sandbox --window-size=1280,800 --timeout=5000 --screenshot=/tmp/page.png https://example.com

Use the timeout only as a diagnostic or when a bounded delay is genuinely appropriate. A fixed wait can make a slow screenshot slower without making it reliable. It does not repair a failed navigation or a broken graphics surface. See Chrome’s Headless mode documentation.

5. Verify screenshot arguments and the output file

  • Path: Confirm the process can write to the requested directory and that you are opening the same path.
  • Extension: Use a supported extension that matches the intended image type: PNG, JPEG/JPG, or WebP.
  • Dimensions: Pass a valid window size, and check the resulting image dimensions. An unexpectedly small image may look blank when scaled or displayed incorrectly.
  • Freshness: Remove or rename the previous output before a diagnostic run, then check its modification time or file size.
  • Consumer: If the file opens in one viewer but appears blank in another, test the actual capture output with a second image viewer or decoder.

6. Isolate graphics problems only when indicated

If navigation, DOM content, timing, and output arguments are correct, compare a simple ordinary page with the page that produces a blank image. A failure limited to WebGL or WebGPU content may point to the graphics path, driver, compositor, or virtualized environment. Check browser logs and GPU diagnostics, then change one graphics variable at a time.

A reported Vercel Labs vgpu issue described blank or incorrect WebGPU screenshots in a particular setup and a workaround involving headed Chromium under Xvfb with graphics flags. That report is environment-specific, not a general fix for blank screenshots. Do not add Xvfb, disable GPU acceleration, or switch to SwiftShader unless a controlled comparison points to that layer. See the vgpu issue tracker.

7. Localize the failure with a small comparison

  1. Capture the same URL with the same binary using the direct CLI.
  2. Capture it through the automation library and compare the actual URL, title, DOM excerpt, dimensions, and image.
  3. Capture a simple page without WebGL/WebGPU, external assets, or complex client scripts.
  4. On the failing page, wait for a known content element and compare the DOM and screenshot again.
  5. Use browser logs and, if relevant, GPU diagnostics to test one suspected graphics change at a time.

If direct capture succeeds and automation fails, investigate the control layer. If a simple page works but a GPU-intensive page fails, investigate graphics presentation. If waiting changes the DOM and the image, refine the readiness condition. These comparisons narrow the cause without relying on a universal flag.

8. Common errors and fixes

Symptom Likely cause What to do
The screenshot is white and the URL is about:blank Navigation did not reach the intended target, or automation is inspecting another tab or context. Log the active page URL and target, await navigation, and verify the browser context and session.
The URL is correct, but the DOM has little or no expected content Navigation completed before a client-rendered page was ready, or page scripts or resources failed. Inspect console and page errors, then wait for a page-specific element and check whether it appears.
The DOM contains the expected content but the image is blank Capture timing, output handling, or graphics presentation may be failing. Check file freshness, dimensions, format, and direct CLI behavior; isolate graphics only if evidence supports it.
A timeout makes no difference The problem is not delayed content, or the page is not reaching the expected state. Check navigation, URL, DOM, and logs instead of increasing the delay again.
The CLI works but automation does not The automation layer may select the wrong target, use a different binary, or capture too early. Compare executable paths, active targets, navigation handling, and readiness conditions.
Only a WebGL/WebGPU page fails A specialized graphics or compositor path may be involved. Compare against a simple page and check browser GPU logs. Test graphics changes in a controlled environment.
The output is missing, unsupported, or appears stale Unwritable path, unsupported extension, wrong consumer, or an old file being inspected. Use a writable path and supported extension, remove old output, and verify the new file and dimensions.

9. Performance, reliability, and cost

Waiting for a specific selector is usually more efficient and repeatable than imposing a long fixed delay on every capture. For dynamic pages, define readiness around the content the screenshot needs; for pages with slow assets, confirm whether those assets are actually required before capture. Each extra wait adds latency, while capturing too early produces a result that may look successful but be unusable.

For reliable automation, record the browser version and executable, requested URL, actual URL, readiness state, screenshot arguments, output dimensions, and relevant browser errors. Reproduce with one variable changed at a time. This makes environment changes—especially browser updates and container graphics changes—easier to diagnose.

DIY capture cost depends on the compute and maintenance needed to run the browser in your environment. Factor in browser installation and updates, execution time, storage, retries, and operational work. A longer timeout or retries should be reserved for failures that those measures can resolve; repeating a broken navigation or graphics path only adds work.

Or skip the browser setup

For a one-call screenshot API, ScreenshotNeo accepts a URL and returns an image or PDF. Its clean-shot process accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the page verdict and billing status in headers. ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.

cURL:

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));

Use a real API key in place of YOUR_API_KEY. The example uses https://example.com; replace it with the page you need. See the ScreenshotNeo API documentation for request options and response details. Sign up for 1,000 free screenshots a month with no card.

FAQ

Is there one Chromium flag that fixes every blank screenshot?

No universal fix is established by the available documentation or issue reports. Identify whether the failure is navigation, readiness, output handling, or graphics before selecting a change.

Does a successful navigation call prove the page is ready?

No. Check the actual URL and DOM, then wait for the content the capture depends on.

Should I always disable GPU acceleration in headless mode?

No. Treat graphics flags as targeted diagnostics when the failure is specific to graphics-heavy content or supported by browser diagnostics.

Which older headless setup may need migration?

From Chrome M132, the old Headless implementation is no longer part of the Chrome binary; the Chromium project points users who rely on it to chrome-headless-shell.