Cameleeringsimoom

simoom / Compare / h2o and nginx vs simoom

A library against two C servers

h2o and nginx vs simoom

nginx and h2o are standalone web servers written in C, with configuration files, static files and proxying. simoom is a library inside your Rust program. They do different jobs, but they all terminate TLS, speak HTTP/2 and serve requests, so benchmark campaign 4 (2026-11-12) measured them side by side, each on the same droplet as simoom. Here is where simoom is ahead, where H2O and nginx are, and how they work together.

16 of 23campaign 4 measures where simoom is ahead of nginxlevel on 4, behind on 3
simoom 2.5x moreHTTPS API, 256 clients: requests/s with 99% within 1 ms, against nginxsimoom 103,311 req/s, nginx 40,777 req/s; H2O: simoom 30% more
43% more than simoomHTTP/3, 64 clients: requests/s with 99% within 1 ms, against H2Osimoom 52,259 req/s, H2O 74,961 req/s
6% less than simoom10,000 idle connections: memory per connection, against nginxsimoom 575 B, nginx 538 B; H2O: simoom 2.9x less

h2o, nginx and simoom at a glance

Wins and losses in one table. H2O at commit f35c735 and nginx from Ubuntu 24.04's packages, each on the same DigitalOcean c-4 droplet as simoom in the same rounds.

h2o / nginxsimoom
What it isstandalone web servers in C: config file, static files, reverse proxya library you build into a Rust program
Static files, reverse proxy, config fileyesno, by design
HTTPS API, 256 clients (req/s, 99% within 1 ms)H2O 79,386; nginx 40,777103,311 (30% more than H2O, 2.5x nginx)
TLS 1.3 handshakes per second, full / resumedH2O 6,615 / 6,458; nginx 3,828 / 4,3149,431 / 10,303
HTTP/3, 64 clients (req/s, 99% within 1 ms)H2O 75,055; nginx not measured (package without HTTP/3)52,259 (H2O 43% more)
HTTP/2, 64 connections x 100 streams (req/s, 99% within 5 ms)H2O 168,962; nginx 87,443152,445 (H2O 11% more)
Status endpoint, 256 clients (req/s, 99% within 1 ms)nginx 57,179; H2O 40,551145,082
64 KB downloads, 64 clients (req/s, 99% within 5 ms)nginx 3,780; H2O 3,7733,786 (level)
Memory per idle connection, 10,000 / 100,000 opennginx 538 / 528 B; H2O 1,681 / 1,680 B575 / 532 B (nginx 6% less at 10,000, level at 100,000)
Traffic spike at 195,000 req/s offered: answers within 1 msnginx 29,135/s; H2O 231/s6,264/s (nginx 4.7x more)
Traffic spike: all answersnginx 160,028/s; H2O 65,179/s159,868/s (level with nginx)
p99 of real requests under 11,000 slow attackersH2O 462 µs; nginx 1,146 µs568 µs (H2O 19% lower)
Peak memory while serving HTTP/3 (PSS)H2O 24,300 KB42,425 KB (H2O 43% less)
Memory at rest (PSS)H2O 5,020 KB; nginx 72,469 KB1,844 KB
Start-up to first answerH2O 14.34 ms; nginx 19.57 ms0.90 ms default build; 1.20 ms full server
Program size, strippednginx 1,283 KB; H2O 1,499 KB735 KB default build; 1,550 KB full server with TLS and HTTP/3

Campaign 4, 2026-11-12: nginx and H2O each ran on the same droplet as simoom in the same rounds; every figure is the median of rounds, and simoom's figure comes from those same runs, so it differs a little from row to row. Details in the benchmarks.

Every measure, head to head

measure (campaign 4)nginxH2O
Status endpoint, 64 clients: requests/s with 99% within 1 mssimoom 26% more108,684 req/s vs simoom 137,443 req/ssimoom 95% more70,543 req/s vs simoom 137,443 req/s
Status endpoint, 256 clients: requests/s with 99% within 1 mssimoom 2.5x more57,179 req/s vs simoom 145,082 req/ssimoom 3.6x more40,551 req/s vs simoom 145,082 req/s
Status endpoint, 1,024 clients: requests/s with 99% within 1 mssimoom 19% more111,554 req/s vs simoom 133,243 req/ssimoom 2.0x more67,646 req/s vs simoom 133,243 req/s
Latency at a fixed 50,000 requests/s (256 clients): p99simoom 2.4x lower839 µs vs simoom 356 µssimoom 4.1x lower1,451 µs vs simoom 355 µs
CPU per request at a fixed 50,000 requests/ssimoom 26% lower32.9 µs vs simoom 24.3 µssimoom 48% lower47.0 µs vs simoom 24.3 µs
HTTPS API, 256 clients: requests/s with 99% within 1 mssimoom 2.5x more40,777 req/s vs simoom 103,311 req/ssimoom 30% more79,386 req/s vs simoom 103,311 req/s
New TLS 1.3 clients: full handshakes per secondsimoom 2.4x more3,695/s vs simoom 8,973/ssimoom 39% more6,464/s vs simoom 8,973/s
Returning TLS 1.3 clients: resumed handshakes per secondsimoom 2.4x more4,216/s vs simoom 9,924/ssimoom 56% more6,351/s vs simoom 9,924/s
A new connection per request, 192 clients: requests/s with 99% within 5 mslevel22,618 req/s vs simoom 22,116 req/ssimoom 17% more18,847 req/s vs simoom 22,116 req/s
Service-to-service HTTP/2, 64 connections x 100 streams: requests/s with 99% within 5 mssimoom 74% more87,443 req/s vs simoom 152,445 req/s11% more than simoom168,962 req/s vs simoom 152,445 req/s
HTTP/3, 64 clients: requests/s with 99% within 1 ms–43% more than simoom74,961 req/s vs simoom 52,259 req/s
64 KB downloads, 64 clients: requests/s with 99% within 5 mslevel3,780 req/s vs simoom 3,786 req/slevel3,773 req/s vs simoom 3,786 req/s
10,000 idle connections: memory per connection6% less than simoom538 B vs simoom 575 Bsimoom 2.9x less1,681 B vs simoom 575 B
100,000 idle connections: memory per connectionlevel528 B vs simoom 532 Bsimoom 3.2x less1,680 B vs simoom 532 B
Traffic spike at 195,000 requests/s offered: answers within 1 ms per second4.7x more than simoom29,135 req/s vs simoom 6,264 req/ssimoom 27x more231 req/s vs simoom 6,264 req/s
Traffic spike: all answers per secondlevel160,028 req/s vs simoom 159,868 req/ssimoom 2.5x more65,179 req/s vs simoom 159,868 req/s
Under 11,000 slow attackers: p99 of real requestssimoom 2.0x lower1,146 µs vs simoom 568 µs19% lower than simoom462 µs vs simoom 568 µs
Start-up, process start to first answer: simoom's default build (HTTP/1.1, HTTP/2, WebSocket)simoom 22x faster19.57 ms vs simoom 0.90 mssimoom 16x faster14.34 ms vs simoom 0.90 ms
Start-up, process start to first answer: simoom's full server (also TLS 1.3 and HTTP/3)simoom 16x faster19.57 ms vs simoom 1.20 mssimoom 12x faster14.34 ms vs simoom 1.20 ms
Program size, stripped: simoom's default build (HTTP/1.1, HTTP/2, WebSocket)simoom 43% smaller1,283 KB vs simoom 735 KBsimoom 2.0x smaller1,499 KB vs simoom 735 KB
Program size, stripped: simoom's full server (also TLS 1.3 and HTTP/3)17% smaller than simoom1,283 KB vs simoom 1,550 KB3% smaller than simoom1,499 KB vs simoom 1,550 KB
Memory at rest (PSS)simoom 39x less70.7 MB vs simoom 1,844 KBsimoom 2.7x less5,028 KB vs simoom 1,844 KB
Peak memory while serving the status endpoint (PSS)simoom 24x less71.3 MB vs simoom 3,008 KBsimoom 2.1x less6,203 KB vs simoom 3,008 KB
Peak memory while serving HTTPS (PSS)simoom 11x less86.2 MB vs simoom 8,089 KBsimoom 17% less9,701 KB vs simoom 8,089 KB
Peak memory while serving HTTP/3 (PSS)–44% less than simoom23.7 MB vs simoom 42.1 MB

Each cell: simoom against that server on the same droplet in the same round, median of rounds, then the server's value against simoom's in the same runs. Every measure and every other server: the benchmarks.

Use them together

Most deployments don't have to choose. nginx or h2o can serve static files and route traffic at the edge while simoom answers the dynamic requests inside your program.

Tested behind front servers

  • Request-smuggling test behind nginx 1.24 and haproxy 2.8: 165 attack streams, 0 desyncs, and 0 requests accepted that both front servers refused.
  • HTTP Garden, as an origin next to nginx, Apache httpd, h2o, hyper, gunicorn and Go: 0 desyncs, 0 dangerous discrepancies.
  • Strict parsing per RFC 9110 and 9112: any message whose framing is ambiguous is refused.

Or skip the hop

  • simoom terminates TLS 1.3 and serves HTTP/2 and HTTP/3 itself, on the same port number, announced with alt-svc.
  • Limits, slow-client guards and overload guards are built in, each with a default.
  • Useful where a second process is a cost: a device, a function, a sidecar.

When to pick nginx or h2o instead

Pick nginx or h2o when

  • You need a web server, not a library: static files, a reverse proxy, load balancing, a config file your operations team already knows.
  • Your traffic regularly exceeds what the machine can carry and in-time answers under overload matter most: in campaign 4's traffic spike nginx answered 4.7x as many requests within 1 ms as simoom.
  • HTTP/3 or HTTP/2 throughput is what you need most: H2O answered 43% more HTTP/3 and 11% more HTTP/2 requests than simoom on the same droplet.
  • Your program isn't written in Rust.
  • You want decades of production use and a large operations community.

Pick simoom when

  • HTTP belongs inside your Rust program, not in a separate process.
  • TLS handshake rate and HTTPS throughput matter: 39% / 56% more full / resumed handshakes and 30% more HTTPS requests per second than H2O on the same droplet, and 2.5x nginx's HTTPS throughput.
  • You want HTTP/3 from a library, with no C toolchain and no third-party crates.
  • Start-up time counts: 0.90 ms to the first answer for the default build, against 14.34 ms for H2O and 19.57 ms for nginx.

How it was measured

Setup

  • DigitalOcean c-4 droplets (4 dedicated vCPUs, Xeon Platinum 8280, Bangalore), Ubuntu 24.04.
  • h2o built from master at commit f35c735 (the project has not tagged a release since 2019 and its docs say to build from source); nginx from Ubuntu 24.04's packages, built without HTTP/3.
  • Open-loop load from two separate machines.

Rules

  • Throughput: the highest steady rate whose p99 stays at or under 1 ms (5 ms for 64 KB bodies and HTTP/2 with 100 streams).
  • Handshakes: closed loop, 192 connections.
  • Campaign 4, 2026-11-12: simoom ran on the same droplet in the same round as nginx and H2O; every figure is the median of rounds.

Benchmark method · the losses · security testing

Common questions

How does simoom's TLS performance compare with h2o?

In benchmark campaign 4 (2026-11-12), with both servers on the same 4-vCPU droplet in the same rounds, simoom completed 9,431 full and 10,303 resumed TLS 1.3 handshakes per second against H2O's 6,615 and 6,458 (39% and 56% more), and answered 103,311 HTTPS requests per second with 99% within 1 ms against H2O's 79,386 (30% more). Its peak memory while serving HTTPS was 7,518 KB against H2O's 9,701 KB.

Is h2o or nginx faster for HTTP/3?

H2O. nginx was not measured on HTTP/3: the Ubuntu 24.04 nginx package used in the benchmark has no HTTP/3 support. In benchmark campaign 4 (2026-11-12), on the same droplet in the same rounds, H2O answered 75,055 HTTP/3 requests per second with 99% within 1 ms against simoom's 52,259 (43% more), and used 43% less peak memory doing it. simoom answered 1.65 times as many HTTP/3 requests as quinn with h3 on its droplet.

Can simoom replace nginx?

Not as a web server. simoom is a library you build into a program: it has no configuration file, no static-file server and no reverse proxy. It replaces the HTTP layer inside your program, and nginx or h2o can still sit in front of it.

Can I run simoom behind nginx or haproxy?

Yes. In a request-smuggling test behind nginx 1.24 and haproxy 2.8, simoom saw 165 attack streams with 0 desyncs, and it accepted none of the requests that both front servers refused. In HTTP Garden, as an origin next to nginx, Apache httpd, h2o, hyper, gunicorn and Go, it had 0 desyncs.

Where is nginx better than simoom?

During traffic spikes: at 195,000 requests per second offered, nginx answered 29,135 requests per second within 1 ms against simoom's 6,264 on the same droplet (4.7 times as many), while both answered about 160,000 per second in total. nginx also used 6% less memory per idle connection with 10,000 open (538 against 575 bytes; level at 100,000), and it is a complete web server with years of operations behind it. H2O, for its part, leads HTTP/2 by 11% and HTTP/3 by 43%. Figures: campaign 4, 2026-11-12.

Serve TLS, HTTP/2 and HTTP/3 from your program

Build with --features full. Over TCP, client and server agree on HTTP/2 or HTTP/1.1 during the TLS handshake; the QUIC listener serves HTTP/3 on the same port number.

let server = Server::builder()
    .listen_tls("0.0.0.0:8443").tls(tls.clone())
    .listen_quic("0.0.0.0:8443").quic_tls(tls)   // HTTP/3, announced with alt-svc
    .service(router)
    .start()?;