Selenium WebDriver Architecture
Selenium WebDriver follows a client-server architecture. Your test script (the client) uses a language-specific binding to construct HTTP requests describing what you want done — 'navigate to this URL,' 'click this element.' Those requests travel to a browser driver (the server) running locally, which translates them into native browser automation calls, executes them, and sends an HTTP response back with the result. Understanding this flow explains why a slow network to your driver, or a driver version mismatch, can break tests that have nothing wrong with the underlying application.
Cricket analogy: The client-server flow is like a captain radioing a field placement instruction to the twelfth man on the boundary, who relays it to the fielder in the correct language and gesture — the instruction has to pass through that middle layer accurately or the fielder ends up in the wrong spot.
The Four Layers: Bindings, Protocol, Driver, Browser
Language bindings are the libraries you actually import — selenium in Python's pip, org.seleniumhq.selenium in Java's Maven, or the selenium-webdriver npm package. They expose an idiomatic API (driver.find_element(), driver.get()) and internally serialize each call into a JSON payload that gets sent as an HTTP request to the driver's local server, typically on a port like 9515 for ChromeDriver. The binding also deserializes the driver's JSON response back into native objects your test code can work with, like a WebElement.
Cricket analogy: Language bindings acting as a translator between your code and JSON-over-HTTP is like a team's analyst converting a coach's verbal instruction into the standardized notation the video analysis software requires — the coach speaks naturally, the analyst handles the format conversion underneath.
The Browser Driver as a Local HTTP Server
Each browser driver — ChromeDriver, geckodriver — is itself a small HTTP server. When your test starts, the binding sends a 'new session' request with a set of desired capabilities (browser name, version, headless flag, and so on); the driver responds with a session ID that's included in every subsequent request. This is why WebDriver is fundamentally stateful: every 'find element,' 'click,' or 'get text' call is scoped to that one browser session, and the driver process stays alive to keep serving requests until the test calls quit().
Cricket analogy: The session ID working like a match ID tying every ball bowled to the correct fixture is exactly how a scoring app keeps deliveries from one Test match from bleeding into another match's scorecard running in parallel.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new")
# Talks to a local ChromeDriver server (default port 9515)
driver = webdriver.Chrome(options=options)
print(driver.session_id) # the ID tagging every subsequent command
# Pointing the same client code at a remote Selenium Grid node instead
remote_driver = webdriver.Remote(
command_executor="http://grid-host:4444/wd/hub",
options=options
)You can inspect the exact HTTP traffic between your script and the driver using a tool like Fiddler or by setting service_args=['--verbose'] on ChromeDriver — every WebDriver command really is a plain JSON POST request under the hood.
Selenium Grid: Distributing the Architecture
Selenium Grid extends this same architecture across multiple machines. In Grid 4, a router receives test requests and forwards them to a distributor, which assigns each session to a matching node — a machine running the actual browser and driver combination requested. From the test script's point of view, nothing changes: it still talks to a single WebDriver endpoint, but that endpoint may now be executing on a Windows machine running Edge while another test in the same suite runs simultaneously on a Linux machine running Firefox.
Cricket analogy: Grid's router-to-node handoff is like a franchise's central scheduling office assigning each net session to whichever ground and coach is free, while every player still just shows up to one booking desk without needing to know which ground they'll end up at.
A driver executable that doesn't match your installed browser's major version is the single most common cause of 'session not created' errors. ChromeDriver in particular is versioned to track Chrome releases closely, so upgrading Chrome without upgrading ChromeDriver will break your test suite.
- WebDriver uses a client-server architecture: language bindings (client) talk HTTP/JSON to a browser driver (server).
- Each browser driver is a small local HTTP server (e.g., ChromeDriver defaults to port 9515).
- A 'new session' request with desired capabilities returns a session ID scoping all later commands.
- WebDriver is stateful — the driver process stays alive for the duration of a session.
- Selenium Grid 4 adds a router and distributor layer to send sessions to matching remote nodes.
- Driver-to-browser version mismatches are a leading cause of session creation failures.
Practice what you learned
1. In Selenium WebDriver's architecture, what role does the browser driver (e.g., ChromeDriver) play?
2. What is returned when a WebDriver client sends a 'new session' request?
3. What is the primary role of the router and distributor in Selenium Grid 4?
4. What commonly causes a 'session not created' error?
5. How do language bindings like Python's selenium package communicate with the browser driver?
Was this page helpful?
You May Also Like
What Is Selenium?
An introduction to Selenium, the open-source framework for automating web browsers, and why it's the backbone of web UI test automation.
Installing Selenium and Browser Drivers
A practical walkthrough of installing the Selenium library and setting up matching browser drivers, including Selenium Manager's automatic driver handling.
Locator Strategies (ID, CSS, XPath)
How to choose and write reliable Selenium locators using ID, CSS selectors, and XPath, and how to weigh their tradeoffs.