ScreenshotNeo

BlogHow-to

How to Fix Chrome Headless Screenshots Returning a Blank Page in Selenium

Diagnose blank Selenium screenshots by checking Chrome’s runtime, page readiness, and viewport—then fix the cause instead of guessing at flags.

By the ScreenshotNeo team4 October 20268 min read

A blank screenshot from Chrome Headless in Selenium is a symptom, not a diagnosis. There is no single flag that fixes every case. First confirm which Chrome binary and arguments actually launched, then check whether the page had rendered the content before capture, and verify the viewport and screenshot dimensions.

Use this sequence to answer “Why is my Selenium Chrome screenshot blank?” without assuming that navigation completion means the application is ready. Chrome’s current Headless mode shares Chrome’s implementation; since Chrome 112 it creates platform windows without displaying them. The older Headless implementation is a separate chrome-headless-shell from Chrome 132.0.6793.0 onward. Chrome Headless mode documentation · Old and new Headless use cases.

1. Record the browser runtime and launch arguments

Before changing settings, record Chrome, ChromeDriver, Selenium, operating system, container or CI environment, the Chrome binary path, and every launch argument. A mismatch between the browser you expect and the binary ChromeDriver starts can make other debugging misleading.

ChromeDriver’s troubleshooting guidance recommends checking chromedriver.log for the Chrome binary and trying that same binary with the same arguments outside WebDriver. This separates browser startup or environment problems from behavior specific to the Selenium harness. ChromeDriver: Chrome doesn’t start or crashes immediately.

For a reproducible comparison, keep a record for each run:

  • Chrome and ChromeDriver versions, Selenium version, and OS or container image.
  • The Chrome binary path and full argument list.
  • Whether the page is also blank in headful Chrome.
  • The navigation and readiness-wait logic.
  • Screenshot width and height, plus the current URL, title, and relevant DOM content.

2. Wait for the application content, not just navigation

A successful navigation call does not prove that a single-page application, delayed API request, or client-side component has finished rendering. Wait for a meaningful condition tied to the content you want to capture, then take the screenshot. Selenium’s older official example waits for a title condition; use an equivalent condition that makes sense for your page rather than treating a title wait as universal. Headless Chrome shell: Selenium examples and FAQ.

This Python example waits for a page-specific element. Replace #main-content with a selector that appears only when the content of interest is ready.

from pathlib import Path

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By

url = "https://example.com"
ready_selector = "#main-content"  # Change to a meaningful page-specific selector.

options = Options()
options.add_argument("--headless")
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
try:
    driver.get(url)
    WebDriverWait(driver, 30).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ready_selector))
    )

    print("URL:", driver.current_url)
    print("Title:", driver.title)
    print("Ready element count:", len(driver.find_elements(By.CSS_SELECTOR, ready_selector)))
    Path("shot.png").write_bytes(driver.get_screenshot_as_png())
finally:
    driver.quit()

presence_of_element_located only confirms that an element exists in the DOM. If the page inserts it before filling it in, wait for a more specific state: visible content, a nonempty text value, a completed application marker, or another condition your site exposes. Avoid replacing a readiness condition with an arbitrary long sleep unless the page offers no observable signal.

3. Inspect the page immediately before capture

Log the current URL and title and inspect the relevant DOM immediately before saving the screenshot. Compare these observations with the pixels:

  • DOM is empty or shows an error/interstitial: investigate navigation, application readiness, redirects, authentication, and whether the environment can reach the site.
  • DOM has the expected content but the screenshot is blank: preserve the log and image dimensions, then compare the same Chrome binary in headful mode. This is a diagnostic inference, not proof of one particular rendering defect.
  • Only some content is missing: check whether the target is outside the viewport, hidden, still loading, or dependent on a later request.

These checks help locate the failure stage; they do not identify a universal root cause. Reproduce with the same binary and arguments outside WebDriver where possible, and change one factor at a time.

4. Set and verify the viewport

Set an explicit window size through Chrome options and inspect the resulting screenshot dimensions. Then check whether the expected content falls inside the captured area. Explicit sizing is a supported control, not a guaranteed fix for every blank image. Chrome documents --window-size with its Headless screenshot command, and ChromeDriver documents passing Chrome arguments through ChromeOptions. Chrome Headless command-line reference · ChromeDriver capabilities and ChromeOptions.

In Selenium, for example:

options.add_argument("--window-size=1440,1000")

Confirm the saved image’s dimensions with your image library or image viewer. If the screenshot has an unexpected size, investigate how the test sets the window and whether a later resize or device emulation changes it. For a command-line reproduction, Chrome supports a screenshot and viewport size together:

chrome --headless --screenshot=screenshot.png --window-size=1440,1000 https://example.com

The CLI’s --timeout sets a maximum wait in milliseconds before capture, even if the page is still loading. It can help reproduce timing behavior, but a page-specific readiness condition in the Selenium test is usually more informative for application content. Chrome Headless command-line reference.

5. Compare Headless and headful runs carefully

Run controlled comparisons, saving logs and dimensions each time. Change only one factor per run:

  1. Headful Chrome versus current unified Headless.
  2. Browser and driver versions, including the actual Chrome binary.
  3. Immediate capture versus a page-specific readiness wait.
  4. Default viewport versus an explicit viewport.
  5. If specifically investigating the legacy implementation, the standalone chrome-headless-shell versus unified Headless.

Do not conflate the old Headless shell with the current unified mode. Chrome’s current documentation describes unified Headless; from Chrome 132.0.6793.0 onward, the old implementation is available only as the standalone shell. Chrome Headless mode · Old and new Headless use cases.

6. Use Chrome flags only when evidence points to them

Do not add flags as a checklist of guesses. The legacy Headless FAQ says --disable-gpu is only needed on Windows as a temporary workaround and is not generally required on other platforms. ChromeDriver warns that using --no-sandbox to work around running Chrome as root is unsupported and highly discouraged; run Chrome as a regular user where possible. Neither flag is a general blank-screenshot fix. Headless Chrome shell FAQ · ChromeDriver startup troubleshooting.

ChromeDriver creates a temporary profile by default. If your diagnosis suggests profile-dependent behavior, ChromeDriver documents a custom user-data-dir and selecting a particular Chrome binary. Treat these as isolation controls; the documentation does not claim either setting fixes blank screenshots by itself. ChromeDriver capabilities and ChromeOptions.

7. Troubleshooting common blank screenshot cases

Symptom Likely area to inspect Next step
Screenshot is blank and DOM is empty Navigation, redirects, access, or app readiness Log URL and title; inspect the page state and environment access before capture.
Screenshot is blank but expected DOM is present Browser rendering, capture timing, or runtime-specific behavior Save logs and dimensions; test the same binary and arguments headful and outside WebDriver.
Only the initial shell or header appears Client-side content has not rendered yet Wait for the content-specific selector or state, not merely navigation completion.
Content is clipped or absent below the fold Viewport and capture area Set an explicit window size and confirm the target lies in the captured viewport.
Behavior changes across machines or CI runs Chrome binary, versions, arguments, or environment Record runtime details; use ChromeDriver logs and reproduce outside WebDriver.
A proposed fix is --disable-gpu Platform-specific legacy workaround Do not treat it as universal; the FAQ describes it as a temporary Windows workaround.
A proposed fix is --no-sandbox because the job runs as root Unsupported root workaround Prefer running Chrome as a regular user; ChromeDriver strongly discourages this shortcut.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. The API accepts the parameter names other screenshot APIs use, which can make switching easier. See the ScreenshotNeo website and API documentation.

cURL:

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

ScreenshotNeo accepts cookie and 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, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. 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, no card required.

Performance, reliability, and cost notes

For Selenium runs, capture only after a useful readiness condition has been met; indiscriminate long waits add time without proving that the content is ready. When comparing runs, save runtime details, logs, viewport, and dimensions so a blank result can be reproduced. The cited Chrome guidance does not provide a universal timeout, a guaranteed flag combination, or a benchmark for this symptom.

ScreenshotNeo pricing is $0 for 1,000 shots per month, Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. ScreenshotNeo bills only clean shots; its response includes X-Page-Verdict and X-Billed headers. These product billing details are separate from the Selenium troubleshooting steps above.

FAQ

Does adding a longer sleep fix an empty screenshot?

Only if the page simply needs more time, and a fixed delay does not establish that the target content is ready. Prefer waiting for a page-specific condition.

Should I switch to the old Headless implementation?

Only when you are deliberately testing the standalone chrome-headless-shell. Current unified Headless is the default model described in Chrome’s documentation; keep the two modes distinct in your diagnosis.

What information should I include in a bug report?

Include Chrome, ChromeDriver, and Selenium versions; OS or container; actual Chrome binary and arguments; readiness wait; current URL and title; whether the DOM has the expected content; and screenshot dimensions. State whether headful mode reproduces the issue.