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

Yes. Selenium WebDriver can capture screenshots while Chrome, Firefox, or another supported browser runs without a visible window. Turn on the browser’s headless option, set a deliberate viewport, navigate to the page, and call the same screenshot method you would use in headed mode. The result can be saved as a file, returned as PNG bytes, or encoded as Base64.

What headless screenshots actually capture

A normal WebDriver screenshot captures the current browsing context: usually the visible viewport of the active tab and frame. Headless mode changes how the browser is displayed, not the screenshot endpoint. Selenium’s documented driver method remains available, and WebElement objects can capture an individual element.

  • Viewport screenshot: the pixels currently visible in the browser window.
  • Element screenshot: the rendered bounds of one WebElement.
  • Full-document screenshot: the entire page height, where the browser binding supports it. Firefox’s Python driver exposes separate full-page methods; an ordinary screenshot should not be assumed to include content below the viewport.

Screenshot behavior also depends on the driver and browser implementation. A W3C-conformant implementation follows the WebDriver specification; unsupported implementations may make a best effort, such as returning the window, visible frame, or display.

Python: capture a headless Chrome screenshot

Install Selenium with pip install selenium. Recent Selenium versions can manage a compatible browser driver automatically when the browser is installed. This complete example writes a PNG and also shows how to obtain bytes and Base64.

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.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import base64

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

with webdriver.Chrome(options=options) as driver:
    driver.get("https://example.com")

    # Viewport screenshot saved directly to disk.
    if not driver.save_screenshot("page.png"):
        raise RuntimeError("Selenium did not save the screenshot")

    # The same image as PNG bytes.
    png_bytes = driver.get_screenshot_as_png()
    with open("page-copy.png", "wb") as image:
        image.write(png_bytes)

    # Base64, useful when embedding in HTML or JSON.
    encoded = driver.get_screenshot_as_base64()
    with open("page-base64.txt", "w", encoding="ascii") as text_file:
        text_file.write(encoded)

    # Element screenshot (after the element has rendered).
    logo = driver.find_element(By.CSS_SELECTOR, "h1")
    logo.screenshot("heading.png")

save_screenshot() returns a Boolean. get_screenshot_as_file(filename) is an equivalent file-oriented API; get_screenshot_as_png() returns binary PNG data, and get_screenshot_as_base64() returns an encoded string.

Headless Firefox and full-page output

Use Firefox options in the same way, then call Firefox’s distinct full-document API when you need content below the viewport.

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("-headless")
options.add_argument("--width=1440")
options.add_argument("--height=900")

with webdriver.Firefox(options=options) as driver:
    driver.get("https://example.com")
    driver.save_full_page_screenshot("full-page.png")

Firefox also provides get_full_page_screenshot_as_file(), get_full_page_screenshot_as_png(), and get_full_page_screenshot_as_base64(). These are not interchangeable with the ordinary viewport methods.

Java, JavaScript, C#, and Ruby examples

Java

import java.io.File;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

ChromeOptions options = new ChromeOptions();
options.addArguments("--headless", "--window-size=1440,900");
ChromeDriver driver = new ChromeDriver(options);
try {
    driver.get("https://example.com");
    File image = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
    // Move image to your desired destination with your Java file API.
    String base64 = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64);
} finally {
    driver.quit();
}

The Java TakesScreenshot interface documents file and Base64 output. Keep the driver open until the capture is complete.

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

JavaScript (Node.js)

const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const fs = require('node:fs');

(async function () {
  const options = new chrome.Options()
    .addArguments('--headless', '--window-size=1440,900');
  const driver = await new Builder()
    .forBrowser('chrome')
    .setChromeOptions(options)
    .build();
  try {
    await driver.get('https://example.com');
    const encoded = await driver.takeScreenshot();
    fs.writeFileSync('page.png', Buffer.from(encoded, 'base64'));
  } finally {
    await driver.quit();
  }
}());

takeScreenshot() returns an encoded screenshot string; decoding it as Base64 produces PNG bytes.

C#

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

var options = new ChromeOptions();
options.AddArgument("--headless");
options.AddArgument("--window-size=1440,900");
using IWebDriver driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://example.com");
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile("page.png");

Ruby

require "selenium-webdriver"

options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless")
options.add_argument("--window-size=1440,900")
driver = Selenium::WebDriver.for(:chrome, options: options)
begin
  driver.navigate.to "https://example.com"
  driver.save_screenshot("page.png")
ensure
  driver.quit
end

Make dimensions reproducible

Headless defaults can differ between environments, which makes visual comparisons unreliable. Set the viewport explicitly in browser arguments or with the binding’s window-management API. For example, Chrome’s headless command-line mode pairs --screenshot with --window-size=412,892 for deterministic dimensions. In Selenium, use a value appropriate to your test:

options.add_argument("--window-size=412,892")

Use the same browser version, device scale factor, fonts, locale, timezone, and page state in visual-regression jobs. A viewport screenshot is not automatically a full-page screenshot; choose the capability that matches the comparison.

Wait for the page you intend to capture

Calling the screenshot method immediately after navigation can produce a loading shell, missing images, or an animation frame. Wait for a meaningful condition rather than an arbitrary long sleep.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

with webdriver.Chrome(options=options) as driver:
    driver.get("https://example.com/dashboard")
    WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "main.dashboard"))
    )
    driver.save_screenshot("dashboard.png")
  • Wait for a selector that proves the relevant UI exists.
  • Scroll elements into view before an element screenshot.
  • Disable or finish animations when pixel-level consistency matters.
  • Ensure authentication, cookies, and test data are established before navigation.

Common failures and fixes

The browser will not start in a server or container

Install a browser and matching driver, use the correct headless argument for that browser, and inspect the driver’s startup error. Linux containers may also require the runtime dependencies expected by the browser. Do not hide startup errors by catching every exception; log the original message.

The image is blank or captures a loading page

Navigation returning does not guarantee that application content has rendered. Add an explicit wait for the page’s content, check for a redirect or authentication failure, and capture only after the relevant element is visible.

The screenshot is the wrong size

Set --window-size=width,height before creating the driver. Record the requested dimensions and browser version with each visual test. Full-screen APIs and viewport arguments are not identical, so use one consistent method.

Only the visible portion was captured

That is expected for a standard driver screenshot. Use Firefox’s full-page methods where available, or implement a controlled scrolling/stitching strategy for browsers that do not expose a full-document endpoint. An element screenshot captures only that element.

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

Images, fonts, or lazy content are missing

Wait for the specific content, scroll lazy regions into view, and verify network access from the execution environment. Cross-origin restrictions, blocked resources, and responsive breakpoints can change the rendered result.

Element capture throws an error

Locate the element after navigation, wait until it is displayed, and avoid capturing an element that has been removed and replaced by a JavaScript framework. Re-find the element immediately before the screenshot.

The file exists but cannot be opened

Check the return value of file methods, confirm the destination directory is writable, and verify that the bytes are PNG data before renaming the file. Base64 output must be decoded before it is written as an image.

Performance, reliability, and output choices

Each screenshot requires a browser session, page load, rendering, and image encoding. Reuse a driver for a batch of URLs when isolation permits, but create a fresh session when cookies or application state must not leak. Keep navigation and wait timeouts bounded, record the URL and viewport with the artifact, and retry only transient navigation failures. A retry cannot fix a deterministic selector, authentication, or missing-resource problem.

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

Choose the output form based on your pipeline:

Need Use
Artifact for a build or review Save PNG directly with save_screenshot or the binding equivalent.
Image processing in memory Request PNG bytes and pass them to your imaging library.
HTML, JSON, or text transport Use Base64, then decode it at the receiving end.
One component only Call the WebElement screenshot method.
Entire document Use a browser-specific full-page method, such as Firefox’s Python API.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you want one request instead of maintaining Selenium browsers. It accepts the cookie or consent banner as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each 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 status. Its MCP tools—take_screenshot, get_page_info, and capture_pdf—let Claude, Cursor, or another MCP client request captures.

See the ScreenshotNeo documentation for parameters. A cURL request is:

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

The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

FAQ

Does headless mode require a display server?

No. The browser renders without opening a visible window, so a desktop display is not required. The browser and its runtime dependencies are still required.

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.

Can Selenium return JPEG or WebP?

The documented Selenium screenshot methods produce PNG data. Convert the PNG afterward if your pipeline requires another format.

Can I capture a PDF with Selenium’s screenshot call?

No. A screenshot endpoint returns an image. PDF generation is a separate browser or service capability.

Is a screenshot proof that the page was fully loaded?

No. It proves only what was rendered at capture time. Your waits and application checks must establish readiness.

Frequently Asked Questions

Which headless argument should I use for Chrome?

Use the Chrome headless argument supported by the browser version in your environment; the examples use --headless and an explicit --window-size.

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

How do I embed a Selenium screenshot in HTML?

Call the binding’s Base64 method, then place the returned value after data:image/png;base64, in an image source.

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.