HTTP/2
HTTP/2 works the moment TLS is on: pass tls: { cert, key } and the same port negotiates HTTP/1.1 or HTTP/2 per connection via ALPN (nghttp2). Plain (non-TLS) listeners stay HTTP/1.1 — there is no cleartext HTTP/2. Your handler doesn't change at all.
Quick look
Generate a dev cert if you don't have one (see the TLS guide for the full walkthrough):
How negotiation works
One port serves both — no second listener, no http2: true flag, no separate handler. Verify what's negotiated:
Note: The
-kflag accepts the self-signed dev cert — local testing only. Production uses a real CA cert; the runtime code is identical.
What changes with HTTP/2 (and what doesn't)
Unchanged — write handlers exactly like the HTTP Server guide:
- Handler signature
(url, method, body)→Response | { status, body } | Promise<…>. Response.json/redirect/ statics,Headers, status codes — all identical.- Limits still apply: 512 connections, 10 MB response body (64 KB stage buffer spills to the heap), 30s watchdog.
- WebSocket upgrades ride the HTTP/1.1 connections on the same port;
h2connections serve normal requests.
What you get from the protocol (handled by nghttp2, not your code):
- Multiplexed streams — many in-flight requests per connection.
- Header compression (HPACK) — smaller repeated headers.
- Server framing (Content-Length / Transfer-Encoding) computed for you, as with HTTP/1.1.
What you don't get:
- No server push API — there is no push handle on the socket.
- No trailers API — set all headers up front in the
Response. - No
fetchupgrade — the client is HTTP/1.1 only. Outboundfetch("https://h2-only.example")fails; call HTTP/1.1-capable endpoints.
Practical example: dual-protocol JSON API
Same file serves both protocols — old clients keep working, new ones get multiplexing:
Behind a platform load balancer that terminates TLS, skip tls entirely and serve plain HTTP — the balancer speaks HTTP/2 to the internet for you.
Troubleshooting
curl reports version 1.1 — the client didn't offer h2, or TLS isn't on. Confirm the tls block is present and retest with curl --http2.
Handshake failure — cert/key unreadable, over size caps (cert ≤ 512KB, key ≤ 256KB), or not a matching pair. Start plain-HTTP first to isolate app bugs, then add TLS.
Outbound fetch to an h2-only host fails — expected: no HTTP/2 client. Proxy through an HTTP/1.1 endpoint or pick a host that still negotiates 1.1.
WebSocket over the TLS port broke after enabling tls — nothing about WS changed; upgrades still happen on HTTP/1.1 connections to the same port. Check the websocket block is present (the upgrade is automatic — no handler branch) and the client uses wss://.
