ScreenshotNeo

BlogHow-to

How to Fix Selenium Screenshots That Show a White Screen in Headless Chrome

Diagnose blank Selenium screenshots by checking browser versions, viewport, page readiness, DOM state, and Chrome logs, with runnable fixes and a decision path.

By the ScreenshotNeo team4 October 20267 min read

A white screenshot in Selenium’s headless Chrome is a symptom, not a diagnosis. First check that Chrome and ChromeDriver have matching major versions, set an explicit viewport, wait for the page-specific content you need, and inspect the live DOM and logs before capture. If the DOM contains the expected page but the image is white, investigate capture and rendering; if the page or app element is missing, investigate navigation, scripts, resources, or site behavior.

This guide uses Python for the runnable Selenium examples. The same diagnostic sequence applies to other Selenium language bindings.

1. Record the environment and match Chrome with ChromeDriver

Before changing flags, record the Selenium version, Chrome version, ChromeDriver version, operating system or container, and exact Chrome arguments. A browser update does not guarantee that the driver was updated with it. Selenium’s Chrome documentation says the browser and driver must match at the major-version level. See Selenium’s Chrome-specific documentation.

python -c "import selenium; print('Selenium', selenium.__version__)"
google-chrome --version
chromedriver --version

Command names can differ by operating system or image; for example, Chrome may be installed as chromium. If the major versions differ, install or pin a compatible Chrome and ChromeDriver pair, then reproduce the failure before changing other variables.

2. Use an explicit headless mode and viewport

Headless sessions should use a deliberate window size. Do not rely on maximizing a window that has no visible desktop. Choose dimensions that match the responsive breakpoint and layout you intend to capture. Chrome’s headless documentation demonstrates setting --window-size for screenshots: Headless Chrome.

Selenium lists --headless=new among commonly used Chrome arguments. Use an argument supported by the installed Chrome version, and avoid treating old flags copied from unrelated setups as universal fixes. See Selenium browser options.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

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

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    driver.save_screenshot("page.png")
finally:
    driver.quit()

This is a minimal capture, not a guarantee that an application’s asynchronous content has finished rendering. Add an explicit wait for the content your test needs, as shown next.

3. Wait for the application state, not just navigation

WebDriver’s normal navigation wait targets document.readyState reaching complete. That does not guarantee that a JavaScript single-page application has fetched data, rendered its main view, or finished a client-side transition. Wait for a page-specific selector or state that proves the target content is visible. Selenium explains navigation and readiness behavior in its browser options documentation.

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

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

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    main = WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
    )
    driver.save_screenshot("page.png")
finally:
    driver.quit()

Replace main with a selector that is meaningful for the target application, such as a page heading or a loaded results container. If the selector exists while its content is still a loading placeholder, wait for the actual state or text you need. A fixed sleep can help isolate a timing hypothesis, but a condition-based wait is more reliable across different load times.

4. Inspect the page and screenshot before guessing at a fix

Capture evidence from the same session immediately before the screenshot. Check the final URL, readiness state, title, body text, expected element visibility, and screenshot dimensions. Chrome also documents dumping the DOM to inspect serialized markup in headless runs: Headless Chrome.

from pathlib import Path
from selenium.webdriver.common.by import By

print("URL:", driver.current_url)
print("Ready state:", driver.execute_script("return document.readyState"))
print("Title:", driver.title)
print("Body text:", driver.find_element(By.TAG_NAME, "body").text[:1000])
print("DOM:", driver.page_source[:2000])

png = Path("page.png")
driver.save_screenshot(str(png))
print("Screenshot bytes:", png.stat().st_size)
print("Viewport:", driver.execute_script(
    "return [window.innerWidth, window.innerHeight, devicePixelRatio]"
))

Interpret the evidence together:

What you find Likely direction Next check
Expected DOM and visible app content; screenshot is white Capture or rendering problem Confirm screenshot dimensions and viewport; reduce to a minimal page and test one browser setting at a time.
Empty or unexpected DOM Navigation, scripts, resources, or site behavior Check current URL, redirects, page errors, and whether the expected app selector appears.
App shell exists but expected content does not Asynchronous app work is incomplete or failed Wait for the real content state; inspect application and browser errors.
Target fails but a simple page works Target-specific behavior or dependency Check target resources, scripts, redirects, and responsive layout at the chosen viewport.

DOM markup and a screenshot answer different questions: markup shows what the document contains, while the image shows what Chrome painted. Chrome’s DOM-dumping option and Selenium’s page readiness guidance are useful diagnostic evidence, but neither alone proves why a particular page is white.

5. Isolate the cause with a small reproducer

  1. Save the versions, OS/container details, viewport, and headless arguments.
  2. Run a capture against a simple known page using the same browser and driver.
  3. Run the same script against the failing URL.
  4. Compare URL, readiness state, title, body and expected selector, logs, viewport, and image output.
  5. Change one variable at a time: first the compatible browser-driver pair, then viewport, then the page-specific wait or other setting under investigation.

A Selenium issue report describes a white-screen symptom in headless mode, but an issue report is one environment’s report, not evidence that every white screenshot has the same cause or workaround: Selenium issue #14544.

6. Troubleshooting common cases

Symptom Cause to check What to do
ChromeDriver reports a session or version error Chrome and ChromeDriver major versions do not match Install or pin a compatible pair, then record both versions with the reproduction.
Screenshot is the wrong size or shows an unexpected layout Headless viewport was implicit or too small for the target breakpoint Set --window-size=WIDTH,HEIGHT explicitly and verify window.innerWidth before capture.
Screenshot shows a loading state or blank app shell Navigation completed before the SPA rendered the requested data Wait for a visible, page-specific element or application state; do not use navigation completion as proof of app readiness.
DOM and screenshot disagree The page source exists but the expected visual content has not painted, or capture setup differs Check element visibility and screenshot dimensions, use a minimal page, and vary one setting at a time.
Only one website produces a white page Site-specific scripts, resources, redirects, or behavior Inspect the final URL and DOM, then compare the target with a simple page in the same session.
Changing a copied headless flag changes behavior Flag support or behavior differs across installed Chrome versions Use the documented argument appropriate to the installed version and reproduce with that exact browser.

7. Performance, reliability, and cost

For repeatable captures, use a compatible, recorded browser-driver pair and a fixed viewport. Condition-based waits avoid both capturing too early and imposing a long fixed delay on pages that are already ready. Keep diagnostic output small but useful: versions, final URL, readiness state, expected selector result, and screenshot dimensions usually make failures easier to classify.

Headless mode removes the need for a visible browser window, but it does not remove the target page’s network, script, or rendering dependencies. A failed load or incomplete app can still produce an empty capture. When running in CI or a container, preserve the relevant browser and driver logs with the failed artifact so the screenshot can be compared with the page state.

Or skip the browser setup

If the task is to capture a website rather than debug a Selenium environment, ScreenshotNeo provides a one-request screenshot API and an MCP server for developers and AI agents. Its API can return PNG, JPEG, WebP, or PDF. 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 and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses identify the page verdict and billing status in headers.
  • An MCP server offers take_screenshot, get_page_info, and capture_pdf tools 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; every feature is on every plan.

Sign up for 1,000 free screenshots a month with no card.

FAQ

Does a white screenshot prove Chrome failed to load the page?

No. Compare the live DOM, expected app element, URL, and screenshot. A white image alone cannot distinguish a missing page from a capture or rendering problem.

Should I add a longer sleep before every screenshot?

Use a wait for the specific content or state you need. A fixed delay is useful as a temporary diagnostic, but it can be too short on slow runs and waste time on fast ones.

Will changing headless mode always fix a blank screenshot?

No universal fix is established by the cited sources. Verify the installed Chrome and driver, use supported arguments, and isolate the cause with a small reproducer.