Free tools Windows power users keep installed
One-click scans. No signup required.
Headless Selenium runs a real Chrome, Firefox, or Edge browser without opening a visible window. WebDriver drives that browser through the vendor’s automation API, so your test exercises the same application code that users receive—not a mocked HTTP client.
For a dependable setup, create a fresh WebDriver session, enable the browser’s headless option, use stable locators and condition-based waits, assert with a test framework, and always call quit(). Selenium Manager normally resolves a compatible driver automatically, while Selenium Grid or RemoteWebDriver adds parallel, cross-platform capacity when one machine is no longer enough.
What headless Selenium actually tests
Headless mode removes the graphical browser window; it does not replace the browser engine. JavaScript execution, DOM updates, cookies, storage, navigation, and network requests still occur in a real browser process. Selenium WebDriver controls that process through browser automation APIs supplied by the browser vendor, which is why the test is close to the application you deploy.
WebDriver itself is a W3C Recommendation. It controls navigation and interaction, but it does not decide whether a test passes, compare expected and actual values, or generate reports. Pair it with a framework such as pytest, JUnit, NUnit, Cucumber, Robot Framework, or your language’s equivalent.
#1 Best Overall
Prerequisites and driver management
Install a language binding
For Python, install Selenium and your test runner in the project’s virtual environment:
python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell: .venvScriptsActivate.ps1
python -m pip install --upgrade pip selenium pytest
The Selenium Python API page currently identifies Selenium 4.49.0 as its latest official release shown there; verify the version appropriate for your project because releases and browser compatibility change.
Do you still need ChromeDriver?
You still need a compatible browser installed. With Selenium releases from 4.6 onward, Selenium Manager is shipped with Selenium and can discover the browser and resolve a matching driver when you instantiate WebDriver. That removes most manual driver-path configuration. If your CI image blocks downloads, uses a nonstandard browser location, or requires a pinned driver, install and expose the driver yourself and pass its service configuration.
A complete Python headless test
This example uses Chrome, an explicit wait, a stable data-test locator, an assertion, and guaranteed teardown. Replace the URL and selector with elements owned by your application.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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
def test_homepage_title():
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")
# Add only flags your CI image actually needs; avoid cargo-cult options.
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
wait = WebDriverWait(driver, 15)
heading = wait.until(
EC.visibility_of_element_located(
(By.CSS_SELECTOR, "[data-test='page-title']")
)
)
assert heading.text.strip() == "Expected title"
finally:
driver.quit()
Run it with pytest -q. The test framework reports failures; WebDriver only performs the browser actions.
Rank #2
Headless options for Chrome, Firefox, and Edge
Use the browser-specific Options class and add its headless argument before creating the driver.
| Browser | Python configuration | Notes |
|---|---|---|
| Chrome | from selenium.webdriver.chrome.options import Optionsoptions.add_argument("--headless=new")driver = webdriver.Chrome(options=options) |
The current Chrome headless mode is --headless=new. Set a deliberate window size for responsive layouts. |
| Edge | from selenium.webdriver.edge.options import Optionsoptions.add_argument("--headless=new")driver = webdriver.Edge(options=options) |
Use the Edge browser installed on the runner; let Selenium Manager resolve the driver when possible. |
| Firefox | from selenium.webdriver.firefox.options import Optionsoptions.add_argument("-headless")driver = webdriver.Firefox(options=options) |
Firefox uses its own command-line argument. Keep the browser version and CI image under control. |
Other useful, browser-supported settings include a fixed viewport, a user-agent override, proxy configuration, downloads, certificates, and logging. Add them only for a test requirement and document why; an option that changes production behavior can hide a real defect.
Build a CI workflow that stays deterministic
- Prepare the runner. Install the chosen browser, Python or another binding, and your project dependencies. Cache packages, not an unverified browser binary.
- Create a new session per test or isolated fixture. A fresh profile prevents cookies, local storage, extensions, and service-worker state from leaking between tests.
- Navigate and synchronize. Call
get(), then wait for the exact condition needed by the next action. - Interact through robust locators. Prefer IDs and names, followed by CSS selectors on stable attributes such as
data-test. Avoid generated class names and absolute XPath. - Assert application state. Check a URL, visible text, enabled control, network-driven result, or other business outcome with the surrounding test framework.
- Collect artifacts on failure. Save a screenshot, page source, browser log, and test metadata in your CI artifact directory before teardown.
- Always call
quit(). It ends the entire WebDriver session and browser process.close()only closes the current window and can leave a session orphaned.
Waits, locators, and the causes of flaky tests
Wait for a condition, not an elapsed time
Use explicit waits such as visibility, presence, clickability, a URL change, or a custom function. A fixed sleep(5) is simultaneously too short for a slow run and wasteful for a fast one. Do not combine implicit and explicit waits: their polling behavior can interact unpredictably and produce long, confusing delays. If a wait times out, identify which condition was false rather than simply increasing the timeout.
wait.until(EC.element_to_be_clickable((By.ID, "save"))).click()
wait.until(EC.url_contains("/account"))
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
Choose selectors that survive UI changes
- Best choices are an element ID, an accessible name, or a test-specific attribute such as
data-test="checkout-submit". - Use a short CSS selector when the attribute is stable and unique.
- Use relative XPath only when the relationship is genuinely semantic and no stable attribute exists.
- Keep locator declarations separate from lookup and interaction code so a UI change has one maintenance point.
Isolate state
Do not reuse one driver across unrelated tests. Clear or recreate profiles when authentication, feature flags, or local storage affect the result. Parallel tests need independent sessions and, when necessary, separate test accounts and data namespaces.
Assertions, screenshots, and diagnostics
WebDriver does not provide assertions or reports. Let the test framework fail the test and publish its normal report. On failure, capture evidence before quitting:
Rank #3
try:
# test actions and assertions
pass
except Exception:
driver.save_screenshot("artifacts/failure.png")
with open("artifacts/failure.html", "w", encoding="utf-8") as f:
f.write(driver.page_source)
raise
finally:
driver.quit()
Selenium’s WebDriver BiDi work adds a bidirectional channel that can stream network requests, console messages, and JavaScript errors. Use those events when a DOM assertion alone cannot explain a failure, such as a blocked API call or a client-side exception.
Headless versus headed execution
| Concern | Headless | Headed |
|---|---|---|
| CI suitability | Runs without a desktop session and is usually easier to containerize. | Needs a display server or virtual display in many CI environments. |
| Debugging visibility | Requires saved screenshots, HTML, logs, or a remote viewer. | You can watch the browser and inspect timing interactively. |
| Rendering | Uses the selected browser version but can expose differences in viewport, fonts, GPU, or window management. | Closer to a developer’s local visual setup, which can conceal CI-only differences. |
| Failure artifacts | Plan screenshot and console capture explicitly. | Live inspection is available, but artifacts are still needed for unattended failures. |
A practical pattern is headless by default in CI and headed mode locally when reproducing a failure. Keep the browser version, viewport, locale, timezone, and feature flags consistent between modes.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesWhen Selenium Grid or RemoteWebDriver is justified
A local headless driver is simplest when one machine and one browser family meet your coverage needs. Selenium Grid and RemoteWebDriver run sessions on other machines, making them appropriate for browser and operating-system matrices or parallel execution.
| Axis | Local headless | Grid or remote browsers |
|---|---|---|
| Browser/OS coverage | Limited to what the runner has installed. | Multiple browser and operating-system combinations can be registered as nodes. |
| Parallel capacity | Bound by CPU, memory, and local browser processes. | Add workers and configure concurrency across machines. |
| Startup and maintenance | Simple setup; your team maintains the runner image. | More infrastructure, routing, browser images, and cleanup to operate. |
| Observability | Direct access to local logs and artifacts. | Centralize session logs, videos or screenshots, and node health information. |
| Network and data isolation | Easy access to private services on the runner’s network. | Requires deliberate routing, credentials, and isolation between workers. |
| Cost | Uses existing CI capacity. | Consumes additional machines or a hosted provider’s capacity. |
Selenium IDE’s runner exposes a Grid server option and worker count, while the Selenium project describes Grid as the component for executing tests across machines. Start with local sessions, then move only the browser/OS combinations or parallel jobs that justify the operational overhead.
Troubleshooting headless failures
“Unable to obtain driver” or browser starts then exits
Confirm that a supported browser is installed and visible to the CI user. Allow Selenium Manager to download its driver, or install a pinned driver and configure its path when outbound downloads are prohibited. Check that browser and driver major versions match.
Rank #4
The page is blank or the test times out
Save a screenshot and page source at the timeout. Verify the URL, DNS, proxy, certificates, authentication, and whether the application blocks the runner’s IP. Wait for the specific application condition rather than document readiness alone; a single-page app can finish the initial load before its data request completes.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchAn element is present but not clickable
It may be covered by a consent dialog, animation, sticky header, or overlay. Wait for clickability, dismiss the overlay through a real user action, scroll the element into view, and inspect the screenshot. Do not force a JavaScript click unless bypassing hit-testing is intentional and documented.
Tests pass locally but fail in CI
Compare browser versions, viewport dimensions, fonts, locale, timezone, environment variables, network access, and test data. Run the failing test alone in a fresh session, enable browser and BiDi logs, and check for shared accounts or leftover processes. Increasing every timeout treats the symptom, not the race.
Headless output differs from headed output
Make viewport and device scale explicit, install the same fonts, and compare screenshots at the same browser version. If the difference is a product bug, keep a regression test for the affected viewport instead of masking it with a special headless-only path.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is a clean page image or PDF rather than interactive assertions, ScreenshotNeo provides a single website-screenshot request. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup 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 result.
See the ScreenshotNeo API documentation for all parameters. A cURL request:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same call in 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)
And 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 also supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper sizes/margins/landscape/page ranges, HTML/CSS-to-image, custom CSS and JavaScript, pre-capture clicks, hidden selectors, waits for selectors/delay/network idle, ad/tracker/request/resource blocking, custom headers/cookies/user agents/Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed public image links, asynchronous jobs with signed webhooks, bulk capture for 100 URLs per call, a usage API, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work.
Best Value
An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients, so an AI agent can request captures without custom browser orchestration. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account to try it.
Frequently Asked Questions
Can I reuse one WebDriver session for an entire test class?
You can, but isolation is safer. Reusing a session allows cookies, storage, windows, and failed state to leak between tests; use a fresh session unless the fixture deliberately owns and resets every piece of state.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →What should I archive when a headless test fails?
At minimum archive the screenshot, page source, test name, browser and driver versions, viewport, console or BiDi events, and the exception stack. Together they distinguish a selector race from a browser, network, or application failure.
Is Selenium suitable for visual regression testing by itself?
Selenium can produce screenshots, but WebDriver does not compare images or publish visual-diff reports. Add a visual comparison tool or service and define tolerances for fonts, anti-aliasing, and responsive breakpoints.
Quick Recap
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.

