# Debugging WebTransport handshake errors: a practical checklist

> Why a WebTransport connection fails and what to check: certificates, serverCertificateHashes, HTTP/3 support, UDP, origin and path, per the spec.

URL: https://webtransport.day/guides/debug-webtransport-handshake-errors/
Updated: 2026-10-28
Facts checked: 2026-10-28

**Short answer:** A failed WebTransport handshake usually means an untrusted certificate, a server without HTTP/3 or WebTransport support, a wrong path, a rejected Origin, or UDP blocked on the network. Browsers expose little detail on purpose, so work through the checklist and test with a known-good server using the browser tester.

## Key facts

- **Usual causes:** Certificate, HTTP/3 or WebTransport support, path or origin, UDP reachability
- **Self-signed certificates:** Need serverCertificateHashes: ECDSA P-256 key, validity under two weeks
- **Error detail:** Intentionally limited by browsers until the endpoint is verified as WebTransport

## Why does a WebTransport connection fail?

A WebTransport session needs several things to line up before the first byte of your data moves: a reachable UDP port, a QUIC connection, a trusted certificate, an HTTP/3 server that supports WebTransport, a path it serves, and an `Origin` it accepts. If any one is wrong, the browser rejects the `ready` promise, and usually `closed` as well, with little explanation.


## What does the browser tell you?

Two things are worth knowing before you start:

- **Synchronous errors.** The `WebTransport` constructor throws `SyntaxError` for an invalid URL, a non-`https` scheme or a fragment in the URL, according to the specification and MDN. Check this before suspecting the network.
- **Asynchronous failures.** On a network or establishment failure the specification moves the transport to a failed state and rejects `closed` and `ready` with a `WebTransportError` (its `source` is `session`). The specification also says the detailed reason is withheld until the endpoint has been verified as a WebTransport server, and certificate errors are fatal. So the message you see is intentionally unspecific. Message strings are not specified, so do not parse them. For reference, when we pointed the [tester](/tools/webtransport-tester/) at a closed port in a Chromium-based browser on 2026-10-28, it reported `WebTransportError: Opening handshake failed.` with source `session`; other browsers and versions may word it differently.

When a connection that was working ends gracefully, `closed` resolves with a close code and reason instead.

## What should I check first?

Work through these in order. Each item is a real constraint from the specification, the IETF draft or the QUIC and HTTP/3 RFCs.

1. **The URL.** Use `https://host:port/path`. WebTransport has no plain-text mode.
2. **UDP reachability.** WebTransport over HTTP/3 uses QUIC, which runs over UDP. If a firewall, security group or network filters UDP to that port, the session cannot establish. Browsers do not document this as a distinct error, so treat it as a practical cause to rule out, not something the error will state.
3. **HTTP/3 and WebTransport enabled on the server.** The draft requires the server to signal extended CONNECT support, HTTP Datagrams and the WebTransport setting, and to allow QUIC datagrams. If it does not, clients must not start a session. A server that only speaks HTTP/1.1 or HTTP/2 over TCP will not work.
4. **The path.** The server must accept WebTransport on the exact path in your URL. The draft suggests a 405 for an unsupported resource, and clients do not follow redirects for this request.
5. **The origin.** Browsers send an `Origin` header. A server may reject origins it does not trust (the draft suggests 403). Testing from a different origin than your app can hide or reveal this.
6. **The certificate.** See the next section.
7. **A proxy or CDN in between.** Several CDNs and load balancers terminate HTTP/3 at the edge or speak HTTP/1.1 to the origin. See [WebTransport and serverless](/serverless/).

## How do certificates work with WebTransport?

A normal connection uses the Web PKI. The server's certificate must be valid for the hostname and chain to a trusted authority, as with any HTTPS site.

For development you can instead supply <Term slug="servercertificatehashes">`serverCertificateHashes`</Term>. According to the specification and MDN:

- The option takes an array of objects with `algorithm: 'sha-256'` and a `value` holding the hash.
- The hash is the SHA-256 of the DER-encoded leaf certificate.
- The certificate must be X.509v3 and currently valid, and its **total validity must not exceed two weeks**.
- The key algorithms an implementation accepts are implementation-defined but must include **ECDSA P-256** and must not include RSA.
- Several hashes can be supplied, which allows rotation.
- When hashes are used, they replace normal certificate validation for that connection, and the connection cannot be pooled.

Generate a compliant certificate and its hash with OpenSSL (an example, adjust names to suit):

```sh
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
  -days 13 -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost" \
  -keyout key.pem -out cert.pem

openssl x509 -in cert.pem -outform der | openssl dgst -sha256
```

Paste the resulting hex into the optional field in the [WebTransport tester](/tools/webtransport-tester/). A 14-day certificate sits exactly at the limit, so we use 13 days to stay under it.

## How do I tell a server problem from a client problem?

Isolate it:

1. Point the [tester](/tools/webtransport-tester/) at a server you do not control that is documented as an echo server (see the [echo server list](/echo-server/)). If it connects, your browser and network are fine.
2. Run your own server locally with a library example ([local testing guide](/guides/test-webtransport-locally/)) and connect with `serverCertificateHashes`. If that works, the problem is hosting, DNS, certificates or the network path.
3. Check the browser's developer tools network or QUIC logging for the failing connection, and the server's own logs for the request and its response status.
4. Test from a different network. If UDP is filtered on one network, it fails there only.

## What about older servers and draft versions?

The IETF protocol document has been revised many times. Library READMEs say which draft they implement (for example `webtransport-go` states draft-16), and some older examples target earlier drafts. A mismatch can look like any other handshake failure. See [client and server libraries](/guides/webtransport-client-libraries/).

## Still stuck?

Reduce the problem to the smallest case: one server, one URL, one browser, one echo message. The [tester](/tools/webtransport-tester/) prints the connection state and any error the browser provides, which is the information to include when you ask for help.

## FAQ

### Why is the error message so vague?

The specification withholds the specific connection error until the endpoint is verified to be a WebTransport server, which prevents pages from using failed connections to probe networks. Expect an opaque failure: the transport's closed and ready promises reject.

### How do I use a self-signed certificate with WebTransport?

Pass serverCertificateHashes when constructing WebTransport. The certificate must be X.509v3 with a validity period of at most two weeks, use an ECDSA P-256 key (RSA keys must not be accepted), and you supply the SHA-256 hash of its DER encoding.

### Does a 403 or 405 from the server show up in the browser?

The draft recommends 403 for a rejected Origin and 405 for an unsupported resource, but the browser reports the failure through the transport promises rather than as an HTTP status you can read.

## Sources

- [W3C: WebTransport (connection errors, serverCertificateHashes)](https://www.w3.org/TR/webtransport/)
- [MDN: WebTransport() constructor](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport/WebTransport)
- [MDN: WebTransportError](https://developer.mozilla.org/en-US/docs/Web/API/WebTransportError)
- [IETF draft-ietf-webtrans-http3: session establishment](https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/)
- [RFC 9114: HTTP/3](https://www.rfc-editor.org/rfc/rfc9114)
- [RFC 9000: QUIC](https://www.rfc-editor.org/rfc/rfc9000)
