HTTP internals compared

Measured on one machine on one day, by standing each server up and probing it — not read from documentation. Reproduce with bash bench/probe-runtimes.sh.

esrunNode.jsBunDeno
Silent connection closed after10.0s88.1s13.0snever (>150s)
Idle keep-alive closed after30.0s6.0s12.0snever (>150s)
HTTP/2 concurrent streams256unlimitedunlimited200
HTTP/2 header list16KBunlimited64KB16KB
HTTP/2 initial window1MB64KB64KB1MB
esrun 0.15.0 · Node 24.14.0 · Bun 1.4.0 (canary) · Deno 2.8.3 · Linux · 2026-08-03

A silent connection — one that completes the TCP handshake and then says nothing — is the cheapest hold on a server there is: one syscall to the peer, no state to keep. Node bounds it at 88s, which is a 60s headersTimeout polled on a 30s interval. Deno does not bound it at all, despite being built on the same HTTP implementation we are; nor does it bound an idle keep-alive connection.

The HTTP/2 rows are read off the wire from each server's SETTINGS frame, which is what it tells every client its limits are. Node and Bun advertise unlimited concurrent streams; the runtimes that cap are the two on hyper.

None of this makes one runtime better than another — Node's 5s keep-alive is more aggressive than ours, and its 88s header bound is looser. It is here so that a number in this documentation can be checked rather than believed.

See also

Last updated on
Edit this page