PRACTICAL GUIDE / Selenium Grid WebSocket reverse proxy

Proxy Selenium Grid WebSockets for WebDriver BiDi

Fix Selenium Grid WebSocket reverse proxy failures for WebDriver BiDi with Upgrade headers, TLS, idle timeouts, session routing, and focused diagnostics.

By The Testing AcademyUpdated July 25, 202621 min read
All field guides
In this guide11 sections
  1. Why Does Selenium Grid HTTP Work While BiDi WebSocket Fails?
  2. How Does a WebDriver BiDi Connection Travel Through Grid?
  3. What Does webSocketUrl Tell the Client?
  4. Which Reverse Proxy Headers Are Required for WebSockets?
  5. How Should TLS and the Public WebSocket URL Be Handled?
  6. How Do Idle Timeouts Break Quiet BiDi Sessions?
  7. Configure and Verify the Route: A Numbered Workflow
  8. How Do You Diagnose 400, 404, 502, and Early Disconnects?
  9. What Should Be Logged Without Exposing Secrets?
  10. Frequently Asked Questions About Selenium Grid WebSocket Reverse Proxying
  11. Why does a returned URL not guarantee a working connection?
  12. Is Connection: upgrade enough without Upgrade: websocket?
  13. Should the proxy rewrite an internal ws URL to public wss?
  14. What does 101 fail to prove?
  15. Are sticky sessions always required?
  16. Can an explicit wait prevent a proxy idle close?
  17. Does closing the socket end the browser session?
  18. Where should security controls sit?
  19. Next Steps for a Proven BiDi Proxy Route

What you will learn

  • Why Does Selenium Grid HTTP Work While BiDi WebSocket Fails?
  • How Does a WebDriver BiDi Connection Travel Through Grid?
  • What Does webSocketUrl Tell the Client?
  • Which Reverse Proxy Headers Are Required for WebSockets?

A Selenium Grid reverse proxy must preserve the WebSocket upgrade for the session-specific BiDi URL returned in webSocketUrl. Configure Grid to advertise its public URL, forward the Upgrade and Connection headers, route /session/{session-id}/se/bidi to the same Grid, set an idle timeout longer than the expected quiet period, and verify the 101 Switching Protocols response before debugging subscriptions. Treat a Selenium Grid WebSocket reverse proxy as a second transport route, not as an automatic extension of working WebDriver HTTP commands.

Why Does Selenium Grid HTTP Work While BiDi WebSocket Fails?

A remote test first uses ordinary HTTP to create a WebDriver session. The client sends POST /session with requested capabilities, the Grid assigns a browser slot, and the response contains the session ID and matched capabilities. Classic commands such as navigation, element lookup, and screenshots continue as separate HTTP requests.

BiDi adds another connection. When the new-session request includes webSocketUrl: true, a supporting remote end returns a WebSocket URL owned by that session. The client then performs an HTTP/1.1 opening handshake against that URL and asks the server to switch protocols. A successful server answers with HTTP 101 Switching Protocols; after that, the connection carries WebSocket frames in both directions for commands, responses, and events.

That split explains the common contradiction: the Grid console loads, POST /session succeeds, and classic WebDriver commands pass, yet BiDi never connects. Those successes prove the HTTP route, session allocation, and at least part of the browser path. They do not prove that a gateway accepts a long-lived connection, forwards upgrade headers, preserves the session resource, or advertises a client-reachable URL.

Do not start by changing Selenium waits. First establish which of these statements is true:

  • No WebSocket URL was returned.
  • A URL was returned but DNS or TLS failed.
  • The opening handshake returned an HTTP status other than 101.
  • The connection opened and then closed before any command.
  • session.subscribe returned a BiDi error.
  • The subscription succeeded but an expected event or test assertion failed.

Each statement names a different boundary. The broader Selenium Grid tutorial covers remote session fundamentals; this guide starts where that HTTP session already works.

How Does a WebDriver BiDi Connection Travel Through Grid?

The WebDriver BiDi transport specification defines WebSocket as the message transport and associates a session WebSocket connection with at most one BiDi session. At a direct WebDriver remote end, the standard resource is /session/{session-id}. Current Selenium Grid WebSocket proxy code adds its own client-facing suffix and routes /session/{session-id}/se/bidi. A reverse proxy in front of Grid must preserve the complete returned Grid path. Do not replace it with the shorter direct-endpoint resource.

In a distributed Grid, the public gateway does not need to know which browser node owns the session. It needs to pass the intact request to the correct Grid entry point. The Selenium Grid architecture documentation describes the Router as the front end and the Session Map as the mapping from session ID to the Node address. That architecture allows the Grid to route session commands after creation. Deployments that externalize this state should also test Selenium Grid Session Map failover without changing the public WebSocket route.

BoundaryRequest or responsibilityEvidence to capture
Test clientCreate HTTP session, read returned URL, start WebSocketSession ID, sanitized returned URL, client error
Public DNS and TLS edgeResolve authority and, for wss, complete TLSResolved address, certificate subject, trust result
Load balancerKeep the route open and pass the request onwardRequest ID, target, status, idle close reason
Reverse proxyPreserve URI and explicit upgrade headersUpstream URI, Upgrade presence, response status
Grid RouterResolve the session and route toward its NodeSession ID, Router log, Session Map result
Node and browser endpointAccept the session resource and exchange framesNode identity, browser process state, close event

The Event Bus has a different job inside distributed Grid. It carries asynchronous events among Grid components; it is not the WebDriver BiDi WebSocket between the test client and browser session. An Event Bus outage can damage Grid control-plane behavior or node registration, but a 502 at the external proxy is not, by itself, evidence of an Event Bus fault. Use Debug Selenium Grid Event Bus Connectivity when component logs show publish, subscribe, registration, or heartbeat symptoms.

Likewise, load-balancer affinity is an architecture decision, not a protocol law. If every front end shares access to the Grid Router and session mapping, the session ID in Grid's /session/{session-id}/se/bidi route can provide the routing key. If a deployment has independent proxy pools with private state or sends upgrades directly to nodes, it may need affinity or another consistent routing mechanism. Document why the chosen path reaches the owning session, especially in a multi-region Selenium Grid architecture.

What Does webSocketUrl Tell the Client?

The client opts in during new-session creation. The Selenium BiDi documentation shows the capability being set in supported bindings, while the specification says a requested boolean true is replaced in the returned capabilities with the constructed session URL.

JSON
{
  "request": {
    "capabilities": {
      "alwaysMatch": {
        "browserName": "firefox",
        "webSocketUrl": true,
        "se:name": "bidi-proxy-canary"
      }
    }
  },
  "responseShape": {
    "value": {
      "sessionId": "11111111-2222-3333-4444-555555555555",
      "capabilities": {
        "webSocketUrl": "wss://grid.example.test/session/11111111-2222-3333-4444-555555555555/se/bidi"
      }
    }
  }
}

The response above is an illustrative shape, not a promise about browser choice, extra capabilities, hostnames, or IDs. Read the actual matched capability from every created session. Do not reconstruct the URL from the Grid base URL if the remote end already supplied one.

Validate four properties immediately:

  1. The value exists and parses as a WebSocket URL.
  2. Its scheme is ws or wss as expected for the client-facing route.
  3. Its authority resolves and is reachable from the test runner, not merely from a Grid container.
  4. Its complete path is retained and, for current Grid, matches /session/{session-id}/se/bidi.

A missing or non-string returned value is a capability or support failure before any proxy handshake. It can reflect a browser, driver, Grid, binding, or deployment capability decision. It is not proof that NGINX stripped Upgrade, because the client has not attempted the upgrade yet.

Use one lifecycle per WebDriver session in the harness unless the binding supplies and manages that lifecycle differently. Migrate Selenium CDP Hooks to WebDriver BiDi explains why endpoint validation belongs in setup. Once the socket works, Enable WebDriver BiDi and Manage Event Subscriptions in Selenium covers the subscription contract in depth.

Which Reverse Proxy Headers Are Required for WebSockets?

Upgrade and Connection are hop-by-hop headers. A reverse proxy does not forward them as ordinary end-to-end headers. The official NGINX WebSocket proxying guide therefore sets them explicitly and uses a map when the upstream Connection value should depend on whether the client requested an upgrade.

Headers do not configure what Grid advertises. The official Selenium Grid CLI options define --external-url and --grid-url. Set the public base URL on the Grid entry point so new sessions return the client-reachable scheme and authority. For a standalone server whose public endpoint is https://grid.example.test, start it with:

Example
java -jar selenium-server-<version>.jar standalone \
  --external-url https://grid.example.test

For a distributed deployment, configure the public Router or entry point with --external-url, and configure each Node with --grid-url https://grid.example.test so node-created endpoint metadata points back through that public Grid route. Docker Selenium exposes the node setting as SE_NODE_GRID_URL=https://grid.example.test. Include any deliberately mounted Grid sub-path in the configured URL. Apply the equivalent version-pinned container arguments or environment variables used by your deployment rather than assuming image defaults.

The following NGINX example keeps the incoming BiDi URI because proxy_pass names only the upstream, not a replacement URI. A dedicated location makes the current Grid client-facing route explicit, while the general location carries New Session and classic WebDriver HTTP commands. The BiDi location also sets an explicit upstream HTTP version for deployments that require it. Review that line against the installed NGINX release, since the official documentation now notes a version boundary for its necessity. Certificate paths, authentication, access rules, resolver settings, and production timeout values remain deployment-specific.

NGINX
http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    upstream selenium_grid_router {
        server selenium-router:4444;
    }

    server {
        listen 443 ssl;
        server_name grid.example.test;

        # ssl_certificate and ssl_certificate_key are deployment-specific.

        location ~ ^/session/[^/]+/se/bidi$ {
            proxy_pass http://selenium_grid_router;
            proxy_http_version 1.1;

            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Host $http_host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;

            proxy_read_timeout 15m;
            proxy_send_timeout 15m;
        }

        location / {
            proxy_pass http://selenium_grid_router;

            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Host $http_host;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

This is a valid teaching baseline, not a universal NGINX Selenium Grid WebSocket configuration. A deployment under a URL prefix, behind another ingress, or using a service mesh needs a deliberate URI and authority policy at every hop. Adjust the route only when the configured external URL and Grid sub-path require it. Validate the rendered configuration with nginx -t, then inspect the live request rather than assuming the file you edited is loaded.

The opening request also contains WebSocket handshake fields such as Sec-WebSocket-Key and Sec-WebSocket-Version; a conforming WebSocket client generates them. Do not synthesize or cache these values in NGINX. The proxy's special responsibility is to make the protocol switch possible and tunnel the accepted connection.

HTTP 101 is the decisive transport checkpoint. It proves the request reached an endpoint that accepted the upgrade. A normal 200 page, a Grid UI response, or a successful /status response is not interchangeable with 101. After 101, stop treating the connection as a sequence of ordinary HTTP responses and begin tracing frames and close behavior.

How Should TLS and the Public WebSocket URL Be Handled?

ws:// is an unencrypted WebSocket connection. wss:// applies TLS. When a CI runner connects across an untrusted network to a TLS-terminating gateway, the returned URL should normally use wss with the public hostname and port covered by a trusted certificate. The gateway may proxy to Grid over ws on a restricted internal network, or it may use TLS again upstream. Choose from the deployment threat model.

The returned authority must describe what the test client can reach. An internal value such as ws://selenium-router:4444/session/.../se/bidi may work inside the container network and fail from hosted CI. A public URL with the wrong nonstandard port can fail at a firewall. A wss URL whose hostname is absent from the certificate fails before NGINX can return an application status.

Forwarded host and scheme headers can help an upstream understand the public request, but they do not determine the webSocketUrl returned by current Grid. Grid constructs that value from its configured external or Grid URL. Do not accept arbitrary client-supplied forwarding headers at face value. Have the edge overwrite them, set --external-url and the applicable node --grid-url or SE_NODE_GRID_URL, then verify the URL returned by a real session.

Path handling deserves the same care. The direct WebDriver BiDi endpoint resource /session/{session-id} and Grid's client-facing /session/{session-id}/se/bidi route are not interchangeable. A trailing slash difference in an NGINX location and proxy_pass, an ingress rewrite, or a mounted Grid sub-path can send the opening handshake somewhere other than the active session resource. Capture both the client-visible path and upstream path in sanitized diagnostics. They should express the intended mapping without losing the session identifier or Grid suffix.

TLS checks should separate these results:

  • DNS could not resolve the public hostname.
  • TCP could not reach the public port.
  • TLS negotiation or certificate validation failed.
  • TLS succeeded but the HTTP upgrade returned a non-101 status.
  • The upgrade succeeded but the WebSocket closed later.

Do not disable certificate verification to turn a production check green. Install the approved trust chain, use the right hostname, and test certificate rotation before expiry. A Production TOML Baseline for Selenium Grid 4 can help keep Grid-side configuration reviewed separately from edge proxy settings.

How Do Idle Timeouts Break Quiet BiDi Sessions?

A WebSocket can be active as a session yet carry no frames for a while. A test may subscribe to console errors, then execute several minutes of behavior that produces none. The absence of events is expected, but an intermediary can interpret silence as an idle connection and close it.

NGINX documents a proxy_read_timeout for the interval in which the proxied server sends no data. Load balancers, ingress controllers, firewalls, NAT devices, and service meshes can have their own limits. The effective limit is the shortest applicable one along the route. Configure each relevant boundary beyond the longest expected quiet period, with a finite operational value and an intentional cleanup policy.

Timeout or lifetimeWhat it controlsWhat changing it cannot repair
Proxy read timeoutSilence while reading from the WebSocket upstreamMissing capability or wrong session path
Load-balancer idle timeoutInactivity observed by that balancing layerInvalid certificate or subscription syntax
Grid session lifetimeContinued ownership of the browser sessionA proxy that strips upgrade headers
Test runner timeoutHow long the job may executeA closed network connection
Selenium explicit waitHow long an application condition is polledIntermediary WebSocket idle expiry
Application timeoutProduct or backend operation lifetimeGrid routing or browser process failure

Ping or keepalive behavior can prevent some idle closures only when every relevant component supports and permits it. Do not assume a Selenium binding, browser endpoint, or intermediary sends the required frame cadence. First measure the quiet interval, inspect who closed the connection, and use a controlled idle probe that exceeds the configured threshold.

Also distinguish deliberate cleanup from an idle fault. The specification states that closing a session-associated WebSocket removes that connection but does not itself end the session. Ending the session closes its associated WebSocket connections. The harness should unsubscribe or release local listeners, close protocol resources through the binding where applicable, call driver.quit, and tolerate a peer that has already closed. Unsubscribe Selenium BiDi Listeners During Teardown covers that ownership in detail.

Configure and Verify the Route: A Numbered Workflow

Use one canary session and follow the same request across every boundary. Do not begin with a full regression suite, because retries and parallel sessions blur the first failure.

  1. Configure the advertised URL and create one session. Set the public Grid entry point's --external-url and any required node --grid-url or SE_NODE_GRID_URL, then send webSocketUrl: true through the same public Grid URL used by CI. Record the timestamp, session ID, matched browser facts, and proxy request ID.

  2. Capture the returned URL exactly. Store a sanitized copy in the test artifact. Confirm its scheme, host, port, and complete path. For current Grid, require /session/{session-id}/se/bidi and compare its session identifier with the HTTP session ID.

  3. Resolve and reach the authority from the runner. Test DNS and TCP from the actual execution network. A check inside the proxy or Grid container does not prove runner access.

  4. Validate TLS before WebSocket behavior. For wss, verify hostname, chain, validity period, and the runner's trust store. Record a TLS error as its own result rather than converting it into a generic socket timeout.

  5. Send a standards-compliant opening handshake. Use the Selenium binding's BiDi connection or a reviewed WebSocket client against the returned URL. Correlate the client attempt with load-balancer, NGINX, and Grid logs.

  6. Require HTTP 101. If the result is 400, 404, 502, or another status, stop before subscription debugging. Compare the client URI, upstream URI, Upgrade and Connection handling, and selected backend.

  7. Hold a controlled idle connection. Keep the canary open beyond the expected quiet interval without application events. Observe whether any intermediary closes it and capture the initiator, elapsed time, and close evidence.

  8. Issue one narrow subscription. After the socket remains open, send a valid session.subscribe command for one event supported in the test matrix. Wait for the command response before triggering browser behavior.

JSON
{
  "id": 1,
  "method": "session.subscribe",
  "params": {
    "events": ["log.entryAdded"]
  }
}
  1. Trigger and correlate one event. Generate a controlled browser action that should emit the subscribed event, then match its receipt to command ID, session ID, context, and timestamps. Follow WebDriver BiDi Event Collection Architecture when scaling beyond this canary.

  2. Close in a known order. Remove the subscription if the API and lifecycle call for it, close the BiDi resource, quit the WebDriver session, and verify Grid releases the slot. Record unexpected close codes without replacing the original failure.

Run this workflow for each supported public route, not only from a laptop on the Grid network. Pair it with Monitor Selenium Grid Health and Readiness Endpoints, while remembering that health success and WebSocket upgrade success prove different contracts.

How Do You Diagnose 400, 404, 502, and Early Disconnects?

Start with the earliest failed checkpoint. Later symptoms often reflect the same root event: a dead session can produce a 404 on a correct path, and a browser crash can make an established socket disappear. Preserve the first error plus correlated infrastructure evidence.

SymptomLikely boundary to inspect firstNext evidence
No returned webSocketUrlCapability negotiation or implementation supportRequested and matched capabilities, browser and Grid logs
DNS or TCP failurePublic authority, network policy, firewallResolver result, destination port, runner route
TLS error before HTTPCertificate, SNI, trust chain, TLS policyHostname, certificate chain, gateway TLS log
HTTP 400 or 426Opening handshake or header processingEffective Upgrade, Connection, HTTP version, upstream response
HTTP 404Lost /session/{id}/se/bidi route, wrong Grid, unknown or ended sessionClient and upstream URI, session state, Router log
HTTP 502 or 504Proxy-to-upstream reachability or response timeoutSelected upstream, connection error, Grid Router health
HTTP 101 then quiet closeIdle policy, peer shutdown, network interruptionClose initiator, elapsed idle time, intermediary logs
Subscription error responseBiDi command, event, scope, or supportCommand ID, sanitized response, supported browser matrix
Subscription succeeds, no eventTrigger timing, context filter, event support, collectorSubscribe response time, trigger time, raw sanitized frames
Event arrives, assertion failsApplication behavior, normalization, or test expectationEvent payload contract, page state, assertion inputs

Keep six failure classes separate in incident reports:

  1. Transport failure: DNS, TCP, TLS, upgrade headers, routing, non-101 response, or an unintended network close.
  2. Missing capability: no usable returned URL or no declared support for the required browser and Grid combination.
  3. Subscription failure: the socket is open, but session.subscribe rejects the event name, parameters, context, or unsupported module.
  4. Grid Event Bus failure: Grid components cannot exchange their internal asynchronous messages or maintain expected component state.
  5. Browser failure: the node remains visible but the browser or driver exits, crashes, or ends the session.
  6. Test assertion failure: transport and event collection succeed, but product state or the harness interpretation does not meet the expectation.

This classification prevents a longer proxy timeout from masking a bad subscription and prevents an application assertion from being filed as "Grid flaky." Selenium Grid 4 Failure Domains and High-Availability Boundaries extends the same boundary discipline to component placement and recovery.

What Should Be Logged Without Exposing Secrets?

A useful trace connects one HTTP new-session request, one returned WebSocket URL, one upgrade attempt, the Grid session, and the selected node. Use synchronized clocks and a request ID propagated across approved boundaries. Correlate Selenium Grid Sessions with OpenTelemetry Traces explains the wider correlation model.

Log these fields where the deployment can collect them safely:

  • WebDriver session ID and a test-run correlation ID.
  • Sanitized WebSocket scheme, authority, port, and path shape.
  • Proxy request ID, upstream target identity, HTTP status, and latency to 101.
  • Grid Router and node identity, plus matched browser name and version.
  • Connection-open, last-frame, close, and session-teardown timestamps.
  • Observed close code and reason, with the initiating side when evidence supports it.
  • Subscription command ID, event names, response class, and context count.

Do not persist authorization headers, cookies, raw query strings that may contain tokens, Sec-WebSocket-Key, test credentials, page bodies, or unredacted network and script events. Hashing a credential is not automatically safe if the value has a small guessable set. Apply redaction before data leaves the process, then restrict artifact access and retention.

Protect the upgrade route with the same authorization intent as session creation. A principal that cannot create or control a session should not gain access by opening its guessed or leaked path. Use a gateway policy that covers both ordinary HTTP and WebSocket handshakes, restrict network sources, rate-limit abusive connection attempts where appropriate, and keep Grid off the public internet. The Grid architecture guide explicitly cautions against wider exposure because Grid controls real browsers with network reach.

Sanitized logging must still be useful. Keep the session ID when policy permits because it is the routing key needed to join evidence; remove URL query values and event content instead of deleting all path context. Test the redactor with representative BiDi network, log, and script events before enabling verbose capture in CI.

Frequently Asked Questions About Selenium Grid WebSocket Reverse Proxying

Why does a returned URL not guarantee a working connection?

It proves capability negotiation produced an endpoint for that session. The test runner must still resolve the public authority, trust TLS, pass every proxy, retain the resource path, and receive 101 from an active session. Validate those steps independently.

Is Connection: upgrade enough without Upgrade: websocket?

No. The opening handshake communicates the requested protocol through Upgrade, while Connection identifies the hop-by-hop upgrade token. NGINX documents explicit forwarding for both. Inspect effective upstream headers because another gateway can remove or replace them after the first proxy.

Should the proxy rewrite an internal ws URL to public wss?

Prefer configuring --external-url and the applicable node Grid URL so the deployment returns the canonical client-facing URL. Forwarded headers alone do not set that capability. Blind string replacement can preserve the wrong port or remove /se/bidi, hiding a broken external-address configuration. If an approved gateway performs transformation, specify and test the full scheme, authority, and path mapping.

What does 101 fail to prove?

It does not prove that a particular BiDi event is implemented, a context filter is valid, the browser will stay alive, an event will be generated, or an application assertion will pass. It proves only that the opening handshake was accepted and the protocol changed on that route.

Are sticky sessions always required?

No. Grid's Router and Session Map can route by the session represented in the request path. Affinity becomes relevant when an outer architecture has state that is not shared or bypasses that routing layer. Prove the chosen topology with concurrent sessions and backend correlation.

Can an explicit wait prevent a proxy idle close?

No. An explicit wait polls an application condition through WebDriver. It does not change a load balancer's observation of BiDi WebSocket frames. Configure network idle policies or verified ping behavior at the responsible boundary, then keep test waits tied to product behavior.

Does closing the socket end the browser session?

The WebDriver BiDi specification says removing a session WebSocket connection does not itself end the session. The harness must still end the WebDriver session and release the Grid slot. Teardown should handle either side closing first without concealing the initial error.

Where should security controls sit?

Keep Grid on restricted networks and put approved controls at the public gateway: TLS, authentication, authorization, source restrictions, and audit logging. Apply them consistently to session creation, classic commands, and WebSocket upgrades. Do not place secrets or sensitive event content in access logs.

Next Steps for a Proven BiDi Proxy Route

Make the canary a deployment check: create one session, require a public and reachable returned URL ending in /session/{session-id}/se/bidi, confirm 101, hold the socket past the intended quiet interval, subscribe to one event, receive it, and end the session cleanly. Run it after proxy, certificate, Grid, browser image, or load-balancer changes.

Keep the evidence boundary-specific. Health checks prove readiness, the opening handshake proves transport, a successful command response proves BiDi messaging, and a received event proves the selected subscription path. None of those alone proves a product assertion.

Once the route is stable, expand event collection and listener lifecycle deliberately rather than adding retries around connection errors. Secure the control plane, preserve the session path, test the public authority from the runner network, and keep HTTP, WebSocket, Grid component, browser, and assertion failures as separate operational signals.

// LIVE COURSE / THE TESTING ACADEMY

Playwright Automation Mastery

Go beyond Selenium. Master Playwright with JS/TS in 90 days.

From the instructor behind this guide.

Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.

Code PROMODE / 10% offJoin the batch

The Testing Academy editorial desk

Practical QA guidance built around test evidence, production tradeoffs, and interview-ready explanations.

Published July 25, 2026 / Reviewed July 25, 2026

PRIMARY REFERENCES

Verify the details at the source

QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.

  1. 01
    Official w3c.github.io reference

    w3c.github.io

    Primary documentation selected and verified for the claims in this guide.

  2. 02
    Official selenium.dev reference

    selenium.dev

    Primary documentation selected and verified for the claims in this guide.

  3. 03
    Official github.com reference

    github.com

    Primary documentation selected and verified for the claims in this guide.

  4. 04
    Official nginx.org reference

    nginx.org

    Primary documentation selected and verified for the claims in this guide.

FAQ / QUICK ANSWERS

Questions testers ask

Why does Selenium Grid return webSocketUrl but the connection still fail?

The capability proves that the session offered a BiDi endpoint, not that every network boundary can reach it. Check whether the returned host, port, scheme, and path are public, then inspect DNS, TLS, proxy upgrade headers, session routing, and the HTTP handshake result before testing any BiDi command.

Which headers must an NGINX reverse proxy forward for WebDriver BiDi?

NGINX must explicitly pass the client's Upgrade value and a Connection value that requests an upgrade because these are hop-by-hop headers. The official pattern uses proxy_set_header Upgrade $http_upgrade and either Connection upgrade or a mapped variable. Confirm the requirements for the deployed NGINX version and surrounding gateway.

Should a proxied BiDi endpoint use ws or wss?

Use wss when the client reaches the public endpoint through TLS, which is the normal choice across untrusted networks. An internal ws hop can be acceptable inside a protected deployment after TLS terminates at the gateway. The returned URL must describe the scheme, authority, and port the client can actually reach.

What does HTTP 101 Switching Protocols prove?

A 101 response proves that the HTTP opening handshake reached an upstream that accepted the WebSocket upgrade on that route. It does not prove that a requested BiDi module is implemented, a subscription is valid, events will arrive, or the connection will survive the configured idle period and later session teardown.

Can a load balancer idle timeout close a healthy BiDi session?

Yes. A BiDi connection can be healthy yet quiet while no subscribed event or command crosses it. Any proxy, gateway, firewall, or load balancer with a shorter idle limit can close that socket. Set each applicable limit beyond the expected quiet period and verify the behavior with a controlled idle test.

Does WebDriver BiDi require sticky sessions on Selenium Grid?

Not universally. The WebSocket request carries a session-specific path, and Grid can route that session through its Router and Session Map. Affinity may still be needed in an architecture whose outer load balancer or proxy cannot share routing state. Base the decision on the actual topology, not a blanket rule.

How do you separate a proxy failure from a subscription failure?

Record the boundary reached. A DNS, TLS, HTTP status, or missing 101 result occurs before BiDi messaging and points to transport or routing. A structured error response to session.subscribe occurs after the WebSocket opened and points to an event name, parameters, scope, browser support, or session state problem.

Is it safe to expose Selenium Grid directly to the internet?

No. Treat Grid as a privileged browser control plane. Keep it on restricted networks and place approved authentication, authorization, TLS, and access controls at the gateway. Limit who can create sessions or open their sockets, patch every component, and avoid logging credentials, cookies, query secrets, or sensitive BiDi event payloads.

RELATED GUIDES

Continue the learning route

GUIDE 01

Migrate Selenium CDP Hooks to WebDriver BiDi

Migrate Selenium CDP hooks to WebDriver BiDi with a capability inventory, event adapters, parity tests, cross-browser rollout, and failure diagnostics.

GUIDE 02

Enable WebDriver BiDi and Manage Event Subscriptions in Selenium

Enable Selenium WebDriver BiDi, verify WebSocket negotiation, scope event subscriptions, prevent races, and clean handlers up deterministically.

GUIDE 03

Selenium Grid Tutorial: Run Tests Across Browsers

Selenium Grid tutorial explaining architecture, setup, remote WebDriver, browser capabilities, parallel execution, Docker, CI, and debugging tips.

GUIDE 04

Monitor Selenium Grid Health and Readiness Endpoints

Learn Selenium Grid health readiness endpoint monitoring with working code, failure cases, debugging steps, and CI evidence for reliable QA automation.

GUIDE 05

Debug Selenium Grid Event Bus Connectivity

Master debug Selenium grid event bus with practical examples, architecture decisions, failure analysis, CI guidance, metrics, and scenario-led interview answers.

GUIDE 06

A Production TOML Baseline for Selenium Grid 4

Build a production Selenium Grid TOML baseline with explicit topology, registration trust, Router authentication, node stereotypes, logs, and health checks.

GUIDE 07

Selenium Grid 4 Failure Domains and High-Availability Boundaries

Map Selenium Grid 4 failure domains across Router, queue, Distributor, Session Map, Event Bus, and Nodes, with recovery and high-availability tradeoffs.

GUIDE 08

Unsubscribe Selenium BiDi Listeners During Teardown

A practical guide to Selenium BiDi event subscription cleanup, with implementation examples, debugging workflows, CI evidence, security controls, and release gates.