WebDriver Architecture, W3C Protocol, Sessions, and Capabilities: Core Concepts and Mental Model
Open the one-line WebDriver constructor and see the system it hides: a language binding acts as the local end, a driver or Grid endpoint acts as the remote end, a new-session negotiation establishes identity and capabilities, and every later command is scoped to that session.
Learning objectives
- Explain the W3C local-end, remote-end, endpoint-node, and intermediary-node model in beginner terms.
- Describe the new-session handshake and the difference between requested and returned capabilities.
-
Explain
alwaysMatch,firstMatch, standard capability names, and vendor-prefixed extension capabilities. - Treat the session ID as a protocol identity rather than a decorative log value.
- Explain how DOM elements cross the protocol boundary as session-scoped element references.
- Distinguish classic WebDriver request/response commands from WebDriver BiDi event streams without conflating them.
1. Why protocol literacy matters
In Chapter 02, webdriver.Chrome() looked like a local
Python object constructor. Operationally, it crosses process
boundaries. The Selenium binding creates a WebDriver new-session
request. A browser driver—or Grid Router acting as an
intermediary—receives that request, negotiates a browser session,
and returns a session ID plus effective capabilities. Every
navigation, element lookup, click, screenshot, and quit is then a
command associated with that session.
When engineers skip this model, remote execution feels “different” even though the same WebDriver semantics are being transported to a different remote end. Grid scheduling, browser-cloud execution, and CI incidents become easier to reason about once you can point to the boundary where a request is created, routed, matched, executed, and returned.
2. Local end, remote end, and transport
The following diagram visualizes the relationships described in Local end, remote end, and transport. Read the nodes in sequence and use the arrows to connect the conceptual state changes to the explanation around the diagram.
flowchart TD T["Test intent"] --> B["Selenium Python binding / local end"] B -->|"HTTP WebDriver command"| R["Driver or Grid Router / remote end"] R --> D["Browser automation endpoint"] D --> A["Application under test"] A --> D D --> R R -->|"WebDriver response"| B B --> E["Assertion + evidence"]
The local end is the client-side implementation that turns binding calls into protocol commands. The remote end receives WebDriver commands. A direct browser driver can be an endpoint node; Grid can act as an intermediary that routes the new-session request to a suitable endpoint. The browser itself is not your Python process, and Grid is not your test runner.
3. New Session is a negotiation, not a launch command with decorations
The W3C New Session command is POST /session. Its
capability request has two important buckets.
alwaysMatch contains requirements that apply to every
candidate. firstMatch is an ordered list of alternative
candidate sets. The remote end validates and merges them, then tries
to find a matching session configuration.
{
"capabilities": {
"alwaysMatch": {
"browserName": "chrome",
"pageLoadStrategy": "normal"
},
"firstMatch": [
{}
]
}
}
This JSON illustrates the wire-level model; normal Selenium code
should use browser Options classes instead of
hand-building the request. Selenium wraps the requested capabilities
into the protocol shape and parses the response back into
driver.capabilities.
4. Requested capabilities versus effective capabilities
A capability is a named property used during session negotiation or
returned to describe the established session. Standard examples
include browserName, browserVersion,
platformName, pageLoadStrategy,
proxy, timeouts, and
unhandledPromptBehavior. The request expresses intent;
the response is evidence of what the remote end actually
established.
Browser-specific capabilities live in a vendor namespace. The
WebDriver specification requires extension-capability keys to
contain a colon, such as goog:chromeOptions or
moz:firefoxOptions. A made-up unprefixed key is not
portable browser configuration; at an endpoint node it is an invalid
argument.
| Question | Request side | Response side |
|---|---|---|
| Which browser do I need? | browserName |
actual browserName |
| Which version actually ran? | optional version constraint | browserVersion |
| Which operating system executed? | optional platformName requirement |
effective platformName |
| Which Chrome arguments were requested? | goog:chromeOptions |
browser/driver-specific returned fields as supported |
5. Session ID is the unit of browser-control ownership
The specification defines a session ID as a UUID-like string that
uniquely identifies an active WebDriver session. Commands such as
navigation or element lookup are routed to a session-specific URI.
When quit() deletes the session, that identity is no
longer active. A later command with the old ID should fail rather
than silently attach to some other browser.
session A: 9f1... -> browser process/context A -> active
DELETE /session/9f1...
session A: 9f1... -> no longer active
GET /session/9f1.../title -> invalid session id
This is why session IDs belong in first-failure evidence: they let you correlate client logs, Grid events, browser evidence, and teardown to the same execution.
6. A WebElement is a protocol handle, not a copied DOM node
When a remote end finds an element, it does not serialize the entire
DOM node into Python. The WebDriver protocol returns an
element-reference object. The W3C identifier key is
element-6066-11e4-a52e-4f735466cecf; its value is a
remote handle known within the session and current browsing-context
state. Selenium wraps that handle in a binding-level
WebElement.
{
"value": {
"element-6066-11e4-a52e-4f735466cecf": "remote-node-id"
}
}
This becomes important later when a DOM rerender invalidates a handle and Selenium reports a stale element reference. Chapter 04 focuses on finding; Chapter 05 on state/interactions; Chapter 28 returns to stale-handle diagnosis.
7. Classic WebDriver versus WebDriver BiDi
Classic WebDriver is primarily command/response: the local end asks for an action or value, and the remote end answers. WebDriver BiDi adds a bidirectional connection where the browser can emit subscribed events such as log or network activity without the client polling each one. BiDi does not erase the WebDriver session model, and CDP is not a cross-browser synonym for BiDi.
Chapter 03 needs only this boundary: do not interpret an HTTP WebDriver response, a BiDi event, and a browser-vendor CDP message as the same transport. Chapter 18 teaches the version-sensitive BiDi APIs.
8. Read-only evidence before mutation
Use the returned session state before adding complex behavior:
from selenium import __version__ as selenium_version
from selenium import webdriver
driver = None
try:
options = webdriver.ChromeOptions()
print("requested=", options.to_capabilities())
driver = webdriver.Chrome(options=options)
print("selenium=", selenium_version)
print("session_id=", driver.session_id)
print("returned=", driver.capabilities)
print("url=", driver.current_url)
print("title=", driver.title)
finally:
if driver is not None:
driver.quit()
The requested dictionary and returned capabilities are not expected to be identical. The response may include defaults and implementation-specific details. Treat the response as negotiated evidence, not as a mirror of your source code.
9. Why this matters in DevOps
A CI failure that says “session not created” is an environment/negotiation incident, not a locator incident. A Grid request waiting for a matching slot is a capability/capacity issue, not an AUT readiness issue. A command sent after teardown is a lifecycle defect, not a browser rendering defect. Protocol vocabulary lets teams classify incidents before changing the wrong layer.
Knowledge check
What does a returned session ID prove?
That a WebDriver session was successfully established and now has a specific protocol identity. It does not prove the AUT is correct or the test will pass.
Why are requested and returned capabilities different concepts?
The request expresses requirements/preferences; the response records the effective session the remote end actually created, including defaults and implementation details.
What is the purpose of firstMatch?
It represents alternative capability candidate sets that can be combined with alwaysMatch during new-session negotiation.
Why must vendor extension capabilities contain a colon?
The W3C WebDriver extension-capability namespace uses a vendor prefix separated by a colon so browser-specific configuration does not collide with standard capability names.
Is a Python WebElement the DOM node itself?
No. It wraps a session-scoped remote element reference that the remote end maps back to a DOM node.
How does classic WebDriver differ from BiDi at a high level?
Classic WebDriver is mainly request/response; BiDi adds a bidirectional event-capable channel. Their transports and feature availability should not be conflated.
Official references and version notes
- Selenium 4.47 release notes — current release baseline used by this chapter.
- Selenium downloads — language bindings and Selenium Server/Grid artifacts.
- W3C WebDriver — current standards-track definition of local/remote ends, capabilities, sessions, commands, errors, and element references.
- Browser options — current Selenium capability/options guidance and browser-specific configuration boundary.
-
Python Remote WebDriver API 4.47.0
—
command_executor, requiredoptions,session_id, and returnedcapabilities. - Getting started with Selenium Grid — Standalone mode and local RemoteWebDriver workflow.
-
Grid CLI options
— current
--host,--port,--selenium-manager,--reject-unsupported-caps, logging, and security-relevant options. - Common WebDriver errors — current Selenium descriptions for invalid session ID, session-not-created, and related failures.
- Finding web elements — Selenium element-reference behavior at binding level.
Version-sensitive behavior was rechecked against current Selenium
and W3C primary documentation on 2026-08-27. The mandatory path
pins Selenium Python 4.47.0, uses Python 3.10+, one installed
supported Chromium-family browser, Java 11+ for the local Selenium
Server/Grid Standalone exercise, Selenium Server 4.47.0, and
loopback-only endpoints at 127.0.0.1. The Grid lab
starts Standalone with --selenium-manager true and
--reject-unsupported-caps true so driver discovery
can remain automatic and an intentionally unavailable capability
is rejected deterministically. WebDriver BiDi is explained only as
a distinct bidirectional protocol boundary here; Chapter 18
teaches its evolving APIs in depth.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.