Skip to content
github-actions[bot] edited this page Oct 2, 2026 · 210 revisions

Throughput and footprint of Titanium as a reverse / edge proxy and as a decrypting (MITM) proxy, measured on the same harness against YARP, nginx, HAProxy, and Envoy where each OS can run them.

RPS is requests per second (gRPC tables use RPC/s). Numbers are Release builds on matched GitHub-hosted runners: Windows and Linux at 4 vCPU / 16 GiB, macOS at macos-15 Apple Silicon 3-core / 7 GB. We compare products within an OS, not OS against OS, so read within one table — absolute RPS is not comparable across operating systems. Not possible means that product cannot run that path on that OS; Not measured means the path exists but no published number yet.

For pooling knobs and certificate first-visit tuning, see Performance and pooling. Laptop cool A/B tables (not publishable) live on Performance Local Lab.

Why this comparison is fair

  • Same load generator, same origin process, and the same warmup / measure windows (2s / 8s) with the same concurrency ramp (8, 16, 32, 64).
  • Every reverse arm is three OS processes: load generator + origin + proxy. Origin-direct omits the proxy; peers are never in-process with the client.
  • Same runner class per table (windows-latest / ubuntu-latest / macos-15). Laptop numbers are never mixed into these tables.
  • Peers use equivalent TLS/ALPN and streaming-friendly settings on the same loopback shape. HAProxy and Envoy are Linux/macOS only — Not possible on Windows, so their columns are omitted there.
  • MITM (HTTPS decryption with forged certificates) is Titanium-only; peers cannot MITM. Those tables show Titanium MITM overhead versus its own reverse path on the same wires.
  • Tiny keep-alive GET (~56-byte JSON) is the industry RPS shape (same class as wrk / TechEmpower). It is also real for small JSON APIs and health checks.
  • Same-protocol H2↔H2 / H3↔H3 on that shape is Titanium’s best case: with interception off, Titanium copies frames instead of decoding and re-encoding headers (peers do a full HTTP decode). Medals there are not the typical reverse-proxy job.
  • Typical reverse is H1 TLS→H1 or H2→H1 (~1.1× YARP on Win/Linux tiny GET). With larger bodies, see Heavier reverse (ratios @ 41f4adee).

How to read the tables

  • Bold RPS is sustain (last concurrency that still met error/latency SLOs). When peak differs, it appears in the same cell as <sub>(peak N · …)</sub> with RSS/CPU. When sustain equals peak, peak is omitted.
  • 🥇 = best among OS-possible peers on that row (highest sustain RPS; on a tie, lower memory then lower CPU%). On tiny Reverse that is Titanium / nginx / YARP (plus HAProxy / Envoy on Linux/macOS). The same rule applies to heavier reverse, architecture-sensitive, TLS-cost, gRPC, and WebSocket peer tables. Gold on tiny GET is that frame-copy best case, not “Titanium is 1.7× on all reverse.” For larger-body H2→H2, see the heavier tables.
  • MITM tables are Titanium-only (no peer medal). Lite÷Reverse / Full÷Reverse = Titanium MITM sustain ÷ Titanium reverse sustain on the same Client×Origin pair — the overhead of decrypting and intercepting versus bare reverse, not versus nginx or YARP.
  • Not possible = cannot do that path. Not measured = path exists but no published number yet. When every row would be Not possible for a peer, that column is omitted and a note above the table explains why.

Contents

Measurement environment

All three OS use public-repo GitHub-hosted runners: Windows / Linux at 4 vCPU / 16 GiB / 14 GB SSD, macOS at macos-15 Apple Silicon (M1) 3-core / 7 GB (pinned, not macos-latest). macOS is Apple Silicon because that is what Mac users run today; the runner sizes differ, which is fine because we compare products within an OS. Same harness knobs: warmup 2s / measure 8s; concurrency 8, 16, 32, 64; median of 3 repeats. Prefer Titanium÷YARP / Titanium÷nginx ratios over absolute RPS.

Laptop High-perf / cool-paired Windows numbers live on Performance Local Lab. Do not mix those absolutes into the tables below.

Windows (GitHub-hosted windows-latest)

OS Windows Server (GitHub-hosted windows-latest)
CPU 4 logical processors
RAM 16 GiB
Runtime .NET 10.0.x
nginx nginx/Windows 1.31.3 (same-OS only; no QUIC)
HAProxy Not possible on Windows (no official port)
Envoy Not possible on Windows (upstream discontinued Windows builds)
YARP Yarp.ReverseProxy 2.3.0
Harness RpsLoadProbe Release; median of 3 repeats

Linux (GitHub-hosted ubuntu-latest)

OS Ubuntu 24.04.x LTS
CPU 4 logical processors (AMD EPYC; runners this pass were 7763 / 9V74)
RAM 16 GiB
Runtime .NET 10.0.11
nginx nginx/1.31.4 (nginx.org mainline, --with-http_v3_module)
HAProxy HAProxy 3.2.23 built with USE_QUIC (GHA; Ubuntu distro 2.8 is not QUIC-capable)
Envoy pinned GitHub release static binary 1.36.7 (HTTP/3 compiled in)
YARP Yarp.ReverseProxy 2.3.0
Harness RpsLoadProbe Release; median of 3 repeats where noted

macOS (GitHub-hosted macos-15, Apple Silicon)

OS macOS 15 (GitHub-hosted macos-15, Apple Silicon arm64)
CPU 3 logical processors (Apple M1)
RAM 7 GB
Runtime .NET 10.0.x
nginx Homebrew nginx with --with-http_v3_module (workflow fails if missing)
HAProxy Homebrew haproxy with USE_QUIC (workflow fails if missing; 3.2.23 osx source fallback)
Envoy Homebrew bottle when present; else pinned darwin-arm64 1.36.7 (official GitHub assets are Linux-only). HTTP/3 compiled in.
MsQuic Homebrew libmsquic + openssl@3 on DYLD_LIBRARY_PATH / DYLD_FALLBACK_LIBRARY_PATH (QuicListener.IsSupported)
YARP Yarp.ReverseProxy 2.3.0
Harness RpsLoadProbe Release; median of 3 repeats where noted

Use the pinned macos-15 label (not macos-latest) so the image does not change between runs.

Saturation control

Calibration for the shared 4 vCPU loopback shape: how close client + origin are to saturated before ranking reverse peers. Tiny keep-alive GET. Blocks A and B: median of 3 repeats @ 41f4adee — 36853300134. Block C is a later re-measure (see that heading). Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Block A % of origin-HttpClient uses median peak RPS. Blocks B/C use peer÷YARP / ÷nginx on median peak (not % of H1 origin). RPS cells embed median RSS / CPU for the proxy child plus its full descendant tree (serve-proxy → nginx master → workers); origin-direct samples the origin child. Product matrices below use matched dotnet-httpclient only (not bombardier).

Block A — H1 plain

Arm Generator Notes
origin-direct dotnet-httpclient No proxy — origin ceiling under the same client used for publishable ratios
origin-direct-bombardier bombardier External client check (CI installs bombardier)
bare-reverse-http1 dotnet-httpclient Thin C# H1 reverse (BareHttp1ReverseProxy) — .NET runtime / loopback ceiling for a three-process reverse hop; not a product peer
nginx-reverse-http1 / yarp-reverse-http1 / twp-reverse-http1 dotnet-httpclient Product peers (medals among these three only)

Windows (windows-latest)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 50,668
(55 MiB / 41.5% CPU)
100.0%
origin-direct-bombardier bombardier 39,281
(56 MiB / 22.5% CPU)
77.5%
bare-reverse-http1 dotnet-httpclient 25,315
(60 MiB / 47.6% CPU)
50.0%
nginx-reverse-http1 dotnet-httpclient 13,599
(125 MiB / 24.9% CPU)
26.8%
yarp-reverse-http1 dotnet-httpclient 21,373
(88 MiB / 49.9% CPU)
42.2%
twp-reverse-http1 dotnet-httpclient 🥇 25,546
(73 MiB / 48.4% CPU)
50.4%

Linux (ubuntu-latest)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 67,804
(80 MiB / 41.9% CPU)
100.0%
origin-direct-bombardier bombardier 40,543
(79 MiB / 34.5% CPU)
59.8%
bare-reverse-http1 dotnet-httpclient 31,377
(69 MiB / 44.2% CPU)
46.3%
nginx-reverse-http1 dotnet-httpclient 🥇 38,409
(75 MiB / 41.2% CPU)
56.6%
yarp-reverse-http1 dotnet-httpclient 27,147
(114 MiB / 50.6% CPU)
40.0%
twp-reverse-http1 dotnet-httpclient 31,727
(85 MiB / 49.7% CPU)
46.8%

macOS (macos-15, Apple Silicon)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 8,344
(89 MiB / 5.9% CPU)
100.0%
origin-direct-bombardier bombardier 39,982
(89 MiB / 23.1% CPU)
479.2%
bare-reverse-http1 dotnet-httpclient 19,997
(82 MiB / 26.7% CPU)
239.7%
nginx-reverse-http1 dotnet-httpclient 🥇 25,507
(68 MiB / 21.2% CPU)
305.7%
yarp-reverse-http1 dotnet-httpclient 21,354
(135 MiB / 37.1% CPU)
255.9%
twp-reverse-http1 dotnet-httpclient 17,976
(106 MiB / 35.9% CPU)
215.4%

On this macOS run origin-direct stayed near idle (5.9% CPU), so the % of origin-HttpClient column is not a ceiling. Rank those Mac peers by RPS.

Reverse peers are about 51–48% of the origin-direct HttpClient peak on the Windows and Linux runners (Win TWP 51.2%, Lin TWP 47.9%). Prefer the % column over absolute RPS across runs. Bare and origin-direct are controls (not medal peers).

Block B — H2 TLS→H1

Peer ratios (÷YARP / ÷nginx) on median peak; RPS cells embed (MiB / CPU%).

Windows (windows-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 7,915
(142 MiB / 24.7% CPU)
0.27× 1.00×
yarp-reverse-http2 dotnet-httpclient 28,934
(95 MiB / 53.9% CPU)
1.00× 3.66×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 35,415
(95 MiB / 50.6% CPU)
1.22× 4.47×

Linux (ubuntu-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 14,622
(100 MiB / 19.3% CPU)
0.51× 1.00×
yarp-reverse-http2 dotnet-httpclient 28,734
(122 MiB / 50.0% CPU)
1.00× 1.97×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 33,677
(116 MiB / 51.1% CPU)
1.17× 2.30×

macOS (macos-15, Apple Silicon)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 19,705
(82 MiB / 22.9% CPU)
0.62× 1.00×
yarp-reverse-http2 dotnet-httpclient 🥇 31,903
(140 MiB / 43.2% CPU)
1.00× 1.62×
twp-reverse-http2-cleartext dotnet-httpclient 31,623
(118 MiB / 41.3% CPU)
0.99× 1.60×

Block C — H3→H1

Same layout as Block B. Requires QuicListener. nginx needs http_v3_module (Windows nginx has no QUIC). HAProxy needs USE_QUIC (GHA Linux/macOS require it). Envoy 1.20+ includes HTTP/3.

Re-measured @ f2061c27 after the per-stream HTTP/3 write scratch — Windows 37005020668, Linux 37005016067. Medals are still nginx / YARP / Titanium only. On the Linux run HAProxy was 22,785 (86 MiB / 27.1% CPU) and Envoy 3,669 (134 MiB / 24.3% CPU); HAProxy is still ahead of Titanium. The TLS-origin twin is not in this table (the 5×5 below stays @ 41f4adee); that re-measure is in Performance Profiling. macOS Apple Silicon: 37011523949.

Windows (windows-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient Not possible (no QUIC) — —
yarp-reverse-http3-cleartext dotnet-httpclient 14,729
(141 MiB / 50.6% CPU)
1.00× —
twp-reverse-http3-cleartext dotnet-httpclient 🥇 15,068
(104 MiB / 45.2% CPU)
1.02× —

Linux (ubuntu-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 0
(peak 15,485 · 106 MiB / 22.3% CPU)
0.84× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 18,511
(183 MiB / 49.5% CPU)
1.00× 1.20×
twp-reverse-http3-cleartext dotnet-httpclient 🥇 20,833
(142 MiB / 49.8% CPU)
1.13× 1.35×

macOS (macos-15, Apple Silicon)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 0
(peak 20,873 · 88 MiB / 19.0% CPU)
1.58× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 13,182
(215 MiB / 42.5% CPU)
1.00× 0.63×
twp-reverse-http3-cleartext dotnet-httpclient 🥇 15,605
(126 MiB / 43.1% CPU)
1.18× 0.75×

Windows — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Client / origin: HTTP version and whether TLS is used (plain = cleartext, TLS = encrypted, QUIC = HTTP/3).

Reverse

Median of 3 repeats on windows-latest (4 vCPU / 16 GiB). Bare reverse 5×5 @ 41f4adee — compare-product 36853254836. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Prefer TWP÷peer ratios over absolute RPS. RPS cells include median RSS / CPU at the peak-RPS step as <br><sub>(MiB / CPU%)</sub>. nginx terminate peers use keepalive 256 + streaming buffers. HAProxy / Envoy are Linux-only peers (no official Windows port). Laptop High-perf / cool-paired numbers stay on the local lab. Product 5×5 is ~56-byte JSON keep-alive GET; H2/H3 same-protocol cells are mostly header work with a tiny body (Titanium best case) — see Why this comparison is fair.

Load generators: Reverse inbound H3 arms use dotnet-httpclient (http_version=3.0, RequestVersionExact). nginx/Windows is same-OS only (no QUIC). HAProxy/Envoy are Linux-only terminate peers.

Client Origin TWP nginx YARP
HTTP/1 · plain HTTP/1 · plain 🥇 25,880
(73 MiB / 47.6% CPU)
14,026
(125 MiB / 24.8% CPU)
21,496
(89 MiB / 49.4% CPU)
HTTP/1 · plain HTTP/1 · TLS 🥇 21,172
(88 MiB / 52.1% CPU)
8,314
(135 MiB / 24.9% CPU)
19,155
(98 MiB / 50.8% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 48,182
(110 MiB / 47.8% CPU)
Not possible (no H2 upstream) 42,621
(93 MiB / 49.2% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 32,909
(118 MiB / 45.1% CPU)
Not possible (no H2 upstream) 30,006
(96 MiB / 46.6% CPU)
HTTP/1 · plain HTTP/3 · QUIC 🥇 18,481
(106 MiB / 50.6% CPU)
Not possible (no H3 upstream) 17,986
(120 MiB / 51.9% CPU)
HTTP/1 · TLS HTTP/1 · plain 🥇 28,020
(87 MiB / 48.7% CPU)
13,783
(142 MiB / 24.6% CPU)
24,447
(101 MiB / 48.5% CPU)
HTTP/1 · TLS HTTP/1 · TLS 🥇 19,084
(88 MiB / 48.2% CPU)
6,979
(143 MiB / 24.6% CPU)
17,016
(103 MiB / 46.6% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 28,276
(111 MiB / 45.6% CPU)
Not possible (no H2 upstream) 26,212
(112 MiB / 46.9% CPU)
HTTP/1 · TLS HTTP/2 · TLS 🥇 33,428
(115 MiB / 43.8% CPU)
Not possible (no H2 upstream) 31,451
(108 MiB / 48% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 16,002
(109 MiB / 50.8% CPU)
Not possible (no H3 upstream) 15,531
(126 MiB / 50.4% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 36,594
(86 MiB / 51.9% CPU)
9,135
(127 MiB / 24.7% CPU)
32,083
(86 MiB / 53.7% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 38,670
(99 MiB / 49.9% CPU)
9,822
(137 MiB / 24.8% CPU)
33,958
(92 MiB / 49.4% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 109,500
(59 MiB / 29.9% CPU)
Not possible (no H2 upstream) 65,061
(94 MiB / 48.2% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 86,785
(68 MiB / 28.5% CPU)
Not possible (no H2 upstream) 51,561
(101 MiB / 44.1% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 34,582
(127 MiB / 53.5% CPU)
Not possible (no H3 upstream) 32,128
(127 MiB / 50.6% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 35,805
(96 MiB / 51.7% CPU)
8,246
(141 MiB / 24.6% CPU)
29,300
(95 MiB / 53.7% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 30,622
(101 MiB / 52.4% CPU)
6,318
(145 MiB / 24.7% CPU)
25,261
(98 MiB / 52.6% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 95,497
(76 MiB / 28.9% CPU)
Not possible (no H2 upstream) 58,094
(103 MiB / 50.6% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 83,912
(74 MiB / 29.6% CPU)
Not possible (no H2 upstream) 49,700
(98 MiB / 49% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 30,854
(135 MiB / 53.3% CPU)
Not possible (no H3 upstream) 26,509
(123 MiB / 53.3% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 20,480
(108 MiB / 43% CPU)
Not possible (no QUIC) 18,402
(146 MiB / 50.5% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 13,870
(110 MiB / 45.2% CPU)
Not possible (no QUIC) 13,299
(146 MiB / 49.3% CPU)
HTTP/3 · QUIC HTTP/2 · plain 🥇 34,720
(126 MiB / 47.6% CPU)
Not possible (no H2 upstream) 23,072
(151 MiB / 49.6% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 34,810
(137 MiB / 47% CPU)
Not possible (no H2 upstream) 21,949
(150 MiB / 47% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 16,836
(121 MiB / 40.3% CPU)
Not possible (no H3 upstream) 12,462
(156 MiB / 47.8% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 36853254836). Lite = no-op handlers (unchanged-lite finish). Full = append-only header mutation (harness: one probe header each way; product: generic append-only relay via MitmCompressedRelayHelper). nginx/HAProxy/Envoy/YARP cannot MITM. Lite÷Reverse / Full÷Reverse vs bare reverse (same job / comparison-group shard). Completion gate: Lite ≥ 0.40× and Full ≥ 0.40× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.60× (no terminate-peer gate).

v1 append-only relay (2026-08-27): Pre-fix H2→H2 Full÷Reverse was 0.13–0.16× (32960766249). Post-fix @ df172718: H2 plain→H2 plain Full 0.77–0.79×, H3→H1 Full 0.91–0.93×, all MITM arms ≥ 0.70× on median of 33041445371, 33055267086, 33055272140.

v2 drop-only + non-unique append (2026-08-27): MitmStaticRebuildHelper rebuilds static HPACK/QPACK after 1–4 unique header drops; trailing non-unique appends stay on compressed relay. @ 41f4adee: all MITM arms ≥ 0.70× on GHA median (33087088466, 33087091622, 33105885748 Linux; 33087085235, 33087088466, 33087091622 Windows). H2 plain→H2 plain Full 0.77–0.79× (Win) / 0.78× (Lin).

Client Origin Lite sustain Full sustain Lite÷Reverse Full÷Reverse
HTTP/1 · plain HTTP/1 · plain 25,573
(77 MiB / 48.5% CPU)
25,028
(78 MiB / 48.6% CPU)
0.99× 0.97×
HTTP/1 · plain HTTP/1 · TLS 21,034
(90 MiB / 51.8% CPU)
20,798
(93 MiB / 51.2% CPU)
0.99× 0.98×
HTTP/1 · plain HTTP/2 · plain 40,940
(102 MiB / 47.9% CPU)
46,410
(114 MiB / 50.1% CPU)
0.85× 0.96×
HTTP/1 · plain HTTP/2 · TLS 32,079
(119 MiB / 47.2% CPU)
31,772
(121 MiB / 44.2% CPU)
0.97× 0.97×
HTTP/1 · plain HTTP/3 · QUIC 18,244
(107 MiB / 52.9% CPU)
17,950
(107 MiB / 51.5% CPU)
0.99× 0.97×
HTTP/1 · TLS HTTP/1 · plain 28,020
(93 MiB / 50.2% CPU)
27,368
(88 MiB / 46.1% CPU)
1× 0.98×
HTTP/1 · TLS HTTP/1 · TLS 19,116
(91 MiB / 50.8% CPU)
18,790
(92 MiB / 46.7% CPU)
1× 0.98×
HTTP/1 · TLS HTTP/2 · plain 28,104
(117 MiB / 45.4% CPU)
27,256
(113 MiB / 46.4% CPU)
0.99× 0.96×
HTTP/1 · TLS HTTP/2 · TLS 33,145
(121 MiB / 46.7% CPU)
32,502
(118 MiB / 46.2% CPU)
0.99× 0.97×
HTTP/1 · TLS HTTP/3 · QUIC 15,961
(111 MiB / 49.4% CPU)
15,795
(116 MiB / 49.6% CPU)
1× 0.99×
HTTP/2 · plain HTTP/1 · plain 35,159
(96 MiB / 55.4% CPU)
34,094
(96 MiB / 54.9% CPU)
0.96× 0.93×
HTTP/2 · plain HTTP/1 · TLS 36,635
(105 MiB / 51.8% CPU)
36,515
(101 MiB / 49.4% CPU)
0.95× 0.94×
HTTP/2 · plain HTTP/2 · plain 84,445
(71 MiB / 40.3% CPU)
80,594
(67 MiB / 41.7% CPU)
0.77× 0.74×
HTTP/2 · plain HTTP/2 · TLS 71,243
(70 MiB / 37.3% CPU)
67,741
(75 MiB / 40% CPU)
0.82× 0.78×
HTTP/2 · plain HTTP/3 · QUIC 34,577
(129 MiB / 53.4% CPU)
33,498
(136 MiB / 55.7% CPU)
1× 0.97×
HTTP/2 · TLS HTTP/1 · plain 33,615
(98 MiB / 53.5% CPU)
33,523
(98 MiB / 54.9% CPU)
0.94× 0.94×
HTTP/2 · TLS HTTP/1 · TLS 28,964
(102 MiB / 53.2% CPU)
28,825
(101 MiB / 55.5% CPU)
0.95× 0.94×
HTTP/2 · TLS HTTP/2 · plain 85,236
(82 MiB / 39% CPU)
82,696
(87 MiB / 40.9% CPU)
0.89× 0.87×
HTTP/2 · TLS HTTP/2 · TLS 68,466
(77 MiB / 38.5% CPU)
65,341
(80 MiB / 40.8% CPU)
0.82× 0.78×
HTTP/2 · TLS HTTP/3 · QUIC 30,486
(124 MiB / 52.6% CPU)
29,988
(137 MiB / 54.1% CPU)
0.99× 0.97×
HTTP/3 · QUIC HTTP/1 · plain 20,113
(119 MiB / 45.2% CPU)
19,554
(117 MiB / 44.1% CPU)
0.98× 0.95×
HTTP/3 · QUIC HTTP/1 · TLS 13,347
(120 MiB / 46.8% CPU)
12,596
(123 MiB / 45.8% CPU)
0.96× 0.91×
HTTP/3 · QUIC HTTP/2 · plain 33,868
(129 MiB / 46.6% CPU)
32,694
(131 MiB / 46.4% CPU)
0.98× 0.94×
HTTP/3 · QUIC HTTP/2 · TLS 33,817
(142 MiB / 47.2% CPU)
32,979
(138 MiB / 46.2% CPU)
0.97× 0.95×
HTTP/3 · QUIC HTTP/3 · QUIC 16,282
(116 MiB / 48.1% CPU)
15,803
(118 MiB / 45.6% CPU)
0.97× 0.94×

Linux — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Reverse

Median of 3 repeats on ubuntu-latest (4 vCPU / 16 GiB). Bare reverse 5×5 @ 41f4adee — compare-product 36853254836. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Linux nginx is the authoritative nginx baseline. HAProxy (3.2 USE_QUIC) and Envoy (GitHub release, HTTP/3 compiled in) run on the same loopback shape as nginx/YARP. nginx terminate peers use keepalive 256 + streaming buffers. The RPS workflow installs nginx.org mainline (http_v3_module), a QUIC-enabled HAProxy, Envoy, and libmsquic. Prefer ratios over absolute RPS. Product 5×5 is ~56-byte JSON keep-alive GET; H2/H3 same-protocol cells are mostly header work with a tiny body (Titanium best case) — see Why this comparison is fair.

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · plain HTTP/1 · plain 35,542
(85 MiB / 50% CPU)
🥇 44,228
(76 MiB / 40.2% CPU)
41,021
(66 MiB / 41.7% CPU)
25,019
(115 MiB / 58.7% CPU)
32,144
(117 MiB / 49.1% CPU)
HTTP/1 · plain HTTP/1 · TLS 53,775
(106 MiB / 48.5% CPU)
🥇 63,560
(93 MiB / 41.2% CPU)
60,644
(70 MiB / 43% CPU)
47,353
(118 MiB / 52.7% CPU)
50,568
(131 MiB / 47.6% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 42,126
(124 MiB / 50.1% CPU)
Not possible (no H2 upstream) 27,642
(66 MiB / 42.6% CPU)
21,554
(116 MiB / 64.2% CPU)
35,843
(123 MiB / 49.5% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 38,179
(142 MiB / 49.2% CPU)
Not possible (no H2 upstream) 34,950
(66 MiB / 43.2% CPU)
25,265
(117 MiB / 59.6% CPU)
35,038
(131 MiB / 47.5% CPU)
HTTP/1 · plain HTTP/3 · QUIC 🥇 50,848
(164 MiB / 54.4% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 29,583
(121 MiB / 48.8% CPU)
43,839
(170 MiB / 47% CPU)
HTTP/1 · TLS HTTP/1 · plain 24,280
(107 MiB / 49.9% CPU)
🥇 28,463
(99 MiB / 41.4% CPU)
27,842
(82 MiB / 43.4% CPU)
17,181
(127 MiB / 58.9% CPU)
20,677
(134 MiB / 51.2% CPU)
HTTP/1 · TLS HTTP/1 · TLS 24,232
(110 MiB / 48.3% CPU)
27,604
(104 MiB / 41.2% CPU)
🥇 27,745
(83 MiB / 42.8% CPU)
19,127
(127 MiB / 54.2% CPU)
21,175
(142 MiB / 49.3% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 69,681
(151 MiB / 49.9% CPU)
Not possible (no H2 upstream) 47,137
(84 MiB / 42.8% CPU)
54,016
(126 MiB / 52.9% CPU)
62,753
(142 MiB / 48.7% CPU)
HTTP/1 · TLS HTTP/2 · TLS 🥇 25,328
(154 MiB / 47.6% CPU)
Not possible (no H2 upstream) 23,716
(83 MiB / 43.3% CPU)
17,369
(126 MiB / 58.4% CPU)
22,175
(140 MiB / 48.9% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 21,737
(150 MiB / 53.2% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 11,174
(130 MiB / 55.1% CPU)
19,611
(160 MiB / 49.6% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 77,078
(120 MiB / 52.3% CPU)
29,472
(79 MiB / 18.5% CPU)
46,455
(67 MiB / 24.4% CPU)
33,118
(118 MiB / 22.9% CPU)
70,854
(112 MiB / 47.4% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 29,364
(125 MiB / 50.8% CPU)
13,245
(101 MiB / 19.5% CPU)
18,930
(71 MiB / 24.6% CPU)
13,147
(118 MiB / 23.5% CPU)
26,309
(128 MiB / 50.9% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 95,132
(90 MiB / 38% CPU)
Not possible (no H2 upstream) 29,728
(66 MiB / 24.3% CPU)
24,027
(115 MiB / 21.6% CPU)
54,791
(132 MiB / 47% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 130,965
(88 MiB / 41.3% CPU)
Not possible (no H2 upstream) 42,285
(68 MiB / 24.5% CPU)
41,750
(118 MiB / 20.9% CPU)
88,980
(132 MiB / 45% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 28,674
(144 MiB / 50.3% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 4,339
(122 MiB / 25% CPU)
26,529
(162 MiB / 46.4% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 39,560
(128 MiB / 51.7% CPU)
17,620
(100 MiB / 19% CPU)
24,964
(81 MiB / 24.3% CPU)
17,152
(129 MiB / 22.7% CPU)
33,979
(122 MiB / 48.6% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 59,236
(133 MiB / 49.1% CPU)
27,000
(112 MiB / 19.1% CPU)
38,045
(86 MiB / 24.6% CPU)
30,525
(128 MiB / 22.6% CPU)
54,530
(126 MiB / 46.9% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 73,087
(95 MiB / 35.9% CPU)
Not possible (no H2 upstream) 21,291
(82 MiB / 24.4% CPU)
17,672
(125 MiB / 22.1% CPU)
40,036
(127 MiB / 47.6% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 61,583
(100 MiB / 35.4% CPU)
Not possible (no H2 upstream) 23,639
(83 MiB / 24.2% CPU)
19,934
(127 MiB / 20.6% CPU)
38,518
(127 MiB / 45.6% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 54,162
(190 MiB / 52.7% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 16,762
(129 MiB / 24.7% CPU)
47,112
(176 MiB / 44.5% CPU)
HTTP/3 · QUIC HTTP/1 · plain 21,551
(145 MiB / 50% CPU)
0
(peak 15,107 · 107 MiB / 22.6% CPU)
🥇 24,083
(86 MiB / 26.1% CPU)
3,989
(132 MiB / 24.5% CPU)
18,996
(186 MiB / 50.3% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 19,461
(163 MiB / 48.5% CPU)
0
(peak 14,824 · 117 MiB / 22.7% CPU)
🥇 21,322
(90 MiB / 28.9% CPU)
4,755
(136 MiB / 24.3% CPU)
18,172
(199 MiB / 49.7% CPU)
HTTP/3 · QUIC HTTP/2 · plain 🥇 60,482
(201 MiB / 55.6% CPU)
Not possible (no H2 upstream) 54,740
(88 MiB / 25.2% CPU)
20
(130 MiB / 0.1% CPU)
51,309
(226 MiB / 47.2% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 25,276
(155 MiB / 50.1% CPU)
Not possible (no H2 upstream) 21,371
(86 MiB / 26.5% CPU)
6
(129 MiB / 0.1% CPU)
21,601
(193 MiB / 48% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 22,007
(157 MiB / 47.7% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 2,856
(130 MiB / 24.9% CPU)
17,736
(207 MiB / 46.7% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 36853254836). Lite = no-op handlers (unchanged-lite finish). Full = append-only header mutation (harness: one probe header each way; product: generic append-only relay via MitmCompressedRelayHelper). nginx/HAProxy/Envoy/YARP cannot MITM. Lite÷Reverse / Full÷Reverse vs bare reverse (same job / comparison-group shard). Completion gate: Lite ≥ 0.40× and Full ≥ 0.40× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.60× (no terminate-peer gate).

v1 append-only relay (2026-08-27): Pre-fix H2→H2 Full÷Reverse was 0.13–0.16× (32960766249). Post-fix @ df172718: H2 plain→H2 plain Full 0.77–0.79×, H3→H1 Full 0.91–0.93×, all MITM arms ≥ 0.70× on median of 33041445371, 33055267086, 33055272140.

v2 drop-only + non-unique append (2026-08-27): MitmStaticRebuildHelper rebuilds static HPACK/QPACK after 1–4 unique header drops; trailing non-unique appends stay on compressed relay. @ 41f4adee: all MITM arms ≥ 0.70× on GHA median (33087088466, 33087091622, 33105885748 Linux; 33087085235, 33087088466, 33087091622 Windows). H2 plain→H2 plain Full 0.77–0.79× (Win) / 0.78× (Lin).

Client Origin Lite sustain Full sustain Lite÷Reverse Full÷Reverse
HTTP/1 · plain HTTP/1 · plain 36,120
(91 MiB / 49.8% CPU)
35,316
(89 MiB / 48.6% CPU)
1.02× 0.99×
HTTP/1 · plain HTTP/1 · TLS 54,813
(111 MiB / 48.3% CPU)
53,246
(110 MiB / 48.4% CPU)
1.02× 0.99×
HTTP/1 · plain HTTP/2 · plain 40,743
(131 MiB / 53% CPU)
39,266
(127 MiB / 53.2% CPU)
0.97× 0.93×
HTTP/1 · plain HTTP/2 · TLS 37,310
(144 MiB / 52.2% CPU)
36,668
(152 MiB / 50.5% CPU)
0.98× 0.96×
HTTP/1 · plain HTTP/3 · QUIC 49,696
(166 MiB / 56% CPU)
49,175
(164 MiB / 55.2% CPU)
0.98× 0.97×
HTTP/1 · TLS HTTP/1 · plain 23,938
(113 MiB / 49.9% CPU)
23,457
(115 MiB / 50% CPU)
0.99× 0.97×
HTTP/1 · TLS HTTP/1 · TLS 24,270
(112 MiB / 48.4% CPU)
24,135
(112 MiB / 48% CPU)
1× 1×
HTTP/1 · TLS HTTP/2 · plain 67,032
(166 MiB / 50.1% CPU)
65,781
(158 MiB / 51% CPU)
0.96× 0.94×
HTTP/1 · TLS HTTP/2 · TLS 23,602
(163 MiB / 49.4% CPU)
23,843
(170 MiB / 48.6% CPU)
0.93× 0.94×
HTTP/1 · TLS HTTP/3 · QUIC 21,552
(151 MiB / 53% CPU)
20,790
(156 MiB / 53.9% CPU)
0.99× 0.96×
HTTP/2 · plain HTTP/1 · plain 76,412
(124 MiB / 52.9% CPU)
73,763
(120 MiB / 53% CPU)
0.99× 0.96×
HTTP/2 · plain HTTP/1 · TLS 28,157
(124 MiB / 52% CPU)
27,650
(129 MiB / 52.1% CPU)
0.96× 0.94×
HTTP/2 · plain HTTP/2 · plain 71,938
(94 MiB / 43.2% CPU)
66,304
(95 MiB / 42.9% CPU)
0.76× 0.7×
HTTP/2 · plain HTTP/2 · TLS 111,003
(103 MiB / 44.1% CPU)
108,771
(106 MiB / 41.6% CPU)
0.85× 0.83×
HTTP/2 · plain HTTP/3 · QUIC 28,202
(145 MiB / 50.3% CPU)
27,788
(144 MiB / 50.5% CPU)
0.98× 0.97×
HTTP/2 · TLS HTTP/1 · plain 37,310
(124 MiB / 52.7% CPU)
37,209
(127 MiB / 52.4% CPU)
0.94× 0.94×
HTTP/2 · TLS HTTP/1 · TLS 57,688
(133 MiB / 49.9% CPU)
55,972
(134 MiB / 49.4% CPU)
0.97× 0.94×
HTTP/2 · TLS HTTP/2 · plain 56,665
(102 MiB / 39.9% CPU)
53,179
(111 MiB / 40.9% CPU)
0.78× 0.73×
HTTP/2 · TLS HTTP/2 · TLS 51,615
(111 MiB / 40.1% CPU)
48,871
(113 MiB / 39.6% CPU)
0.84× 0.79×
HTTP/2 · TLS HTTP/3 · QUIC 52,800
(185 MiB / 51.8% CPU)
51,956
(189 MiB / 52.1% CPU)
0.97× 0.96×
HTTP/3 · QUIC HTTP/1 · plain 20,377
(143 MiB / 50.5% CPU)
20,027
(140 MiB / 50.4% CPU)
0.95× 0.93×
HTTP/3 · QUIC HTTP/1 · TLS 18,610
(168 MiB / 48.9% CPU)
18,240
(168 MiB / 48.8% CPU)
0.96× 0.94×
HTTP/3 · QUIC HTTP/2 · plain 58,870
(200 MiB / 57.1% CPU)
59,642
(195 MiB / 56.5% CPU)
0.97× 0.99×
HTTP/3 · QUIC HTTP/2 · TLS 25,006
(155 MiB / 50.1% CPU)
24,294
(158 MiB / 50.2% CPU)
0.99× 0.96×
HTTP/3 · QUIC HTTP/3 · QUIC 21,690
(163 MiB / 50.4% CPU)
20,322
(161 MiB / 50.5% CPU)
0.99× 0.92×

macOS — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Reverse

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB). Bare reverse 5×5 @ 41f4adee — compare-product 36982444617. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Prefer TWP÷peer ratios over absolute RPS. RPS cells include median RSS / CPU at the peak-RPS step as <br><sub>(MiB / CPU%)</sub>. The RPS workflow installs Homebrew nginx (http_v3_module), Homebrew HAProxy with USE_QUIC (3.2 source fallback), Envoy (Homebrew bottle or pinned darwin-arm64 1.36.7), Homebrew libmsquic (+ DYLD_*), and YARP. Use the pinned macos-15 label, not macos-latest. Product 5×5 is ~56-byte JSON keep-alive GET; H2/H3 same-protocol cells are mostly header work with a tiny body (Titanium best case) — see Why this comparison is fair.

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · plain HTTP/1 · plain 27,016
(112 MiB / 37.6% CPU)
30,305
(68 MiB / 21.1% CPU)
🥇 37,730
(82 MiB / 28.5% CPU)
25,845
(111 MiB / 46.9% CPU)
27,245
(148 MiB / 35.8% CPU)
HTTP/1 · plain HTTP/1 · TLS 17,198
(132 MiB / 37.6% CPU)
17,074
(80 MiB / 21% CPU)
🥇 31,358
(88 MiB / 32.3% CPU)
23,774
(113 MiB / 45.6% CPU)
19,704
(159 MiB / 35.1% CPU)
HTTP/1 · plain HTTP/2 · plain 32,773
(127 MiB / 29.2% CPU)
Not possible (no H2 upstream) 20,883
(84 MiB / 27.6% CPU)
21,625
(111 MiB / 31.8% CPU)
🥇 41,904
(163 MiB / 38.8% CPU)
HTTP/1 · plain HTTP/2 · TLS 33,673
(168 MiB / 37.2% CPU)
Not possible (no H2 upstream) 38,027
(85 MiB / 35.8% CPU)
25,071
(112 MiB / 45.3% CPU)
🥇 38,644
(162 MiB / 38.6% CPU)
HTTP/1 · plain HTTP/3 · QUIC 10,473
(133 MiB / 42.8% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 22,465
(116 MiB / 46.9% CPU)
🥇 33,872
(175 MiB / 42.6% CPU)
HTTP/1 · TLS HTTP/1 · plain 13,136
(124 MiB / 32% CPU)
🥇 19,550
(84 MiB / 22.3% CPU)
10,364
(101 MiB / 14.3% CPU)
5,884
(122 MiB / 22.7% CPU)
14,207
(157 MiB / 32.7% CPU)
HTTP/1 · TLS HTTP/1 · TLS 16,562
(152 MiB / 36.9% CPU)
15,789
(89 MiB / 22.6% CPU)
🥇 24,029
(103 MiB / 32.1% CPU)
11,615
(123 MiB / 38.3% CPU)
15,260
(166 MiB / 37.4% CPU)
HTTP/1 · TLS HTTP/2 · plain 12,874
(156 MiB / 29.4% CPU)
Not possible (no H2 upstream) 17,595
(101 MiB / 28.5% CPU)
17,894
(121 MiB / 42% CPU)
🥇 25,472
(187 MiB / 26% CPU)
HTTP/1 · TLS HTTP/2 · TLS 7,448
(209 MiB / 17.2% CPU)
Not possible (no H2 upstream) 🥇 20,146
(101 MiB / 30.3% CPU)
14,340
(121 MiB / 39.7% CPU)
4,544
(190 MiB / 9.9% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 16,589
(156 MiB / 44.3% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 15,066
(125 MiB / 42.2% CPU)
🥇 20,502
(173 MiB / 39.9% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 45,158
(126 MiB / 40% CPU)
29,051
(69 MiB / 23.5% CPU)
29,130
(87 MiB / 23.7% CPU)
15,366
(111 MiB / 29.5% CPU)
35,765
(147 MiB / 39.4% CPU)
HTTP/2 · plain HTTP/1 · TLS 30,201
(140 MiB / 42.2% CPU)
18,471
(81 MiB / 23% CPU)
28,685
(92 MiB / 27.6% CPU)
13,578
(113 MiB / 27.4% CPU)
🥇 33,191
(159 MiB / 43.4% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 98,980
(116 MiB / 30% CPU)
Not possible (no H2 upstream) 34,877
(87 MiB / 27.6% CPU)
23,568
(109 MiB / 25.8% CPU)
58,570
(151 MiB / 42.5% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 107,876
(125 MiB / 32.2% CPU)
Not possible (no H2 upstream) 33,056
(89 MiB / 26.7% CPU)
22,703
(111 MiB / 26.9% CPU)
83,722
(157 MiB / 42.3% CPU)
HTTP/2 · plain HTTP/3 · QUIC 14,700
(143 MiB / 35.1% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 10,618
(114 MiB / 30% CPU)
🥇 22,834
(163 MiB / 43.6% CPU)
HTTP/2 · TLS HTTP/1 · plain 35,882
(131 MiB / 41.1% CPU)
23,621
(81 MiB / 22.6% CPU)
34,868
(101 MiB / 25.3% CPU)
20,382
(121 MiB / 30.2% CPU)
🥇 39,986
(153 MiB / 42.5% CPU)
HTTP/2 · TLS HTTP/1 · TLS 22,919
(137 MiB / 42.2% CPU)
🥇 23,815
(88 MiB / 22% CPU)
23,102
(104 MiB / 27.6% CPU)
12,429
(122 MiB / 27.6% CPU)
18,991
(162 MiB / 37.8% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 68,025
(131 MiB / 28.6% CPU)
Not possible (no H2 upstream) 25,181
(101 MiB / 27.5% CPU)
19,086
(119 MiB / 24.2% CPU)
39,591
(161 MiB / 35.7% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 94,817
(136 MiB / 32.7% CPU)
Not possible (no H2 upstream) 47,241
(102 MiB / 29% CPU)
31,349
(119 MiB / 26% CPU)
68,743
(157 MiB / 44.1% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 20,008
(160 MiB / 47.8% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 10,706
(123 MiB / 30.9% CPU)
🥇 29,346
(168 MiB / 42.5% CPU)
HTTP/3 · QUIC HTTP/1 · plain 12,512
(138 MiB / 42.8% CPU)
0
(peak 18,612 · 88 MiB / 18.9% CPU)
🥇 30,575
(101 MiB / 29.4% CPU)
5,838
(129 MiB / 37.9% CPU)
18,511
(213 MiB / 45% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 13,728
(152 MiB / 39.5% CPU)
0
(peak 18,482 · 93 MiB / 20.6% CPU)
🥇 22,531
(104 MiB / 31.5% CPU)
5,728
(130 MiB / 31% CPU)
12,918
(274 MiB / 45.7% CPU)
HTTP/3 · QUIC HTTP/2 · plain 20,661
(147 MiB / 47.8% CPU)
Not possible (no H2 upstream) 🥇 43,121
(101 MiB / 31% CPU)
12,596
(125 MiB / 39% CPU)
28,622
(219 MiB / 45.1% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 20,776
(147 MiB / 49.2% CPU)
Not possible (no H2 upstream) 🥇 23,721
(102 MiB / 28.4% CPU)
7,012
(125 MiB / 37.3% CPU)
23,520
(222 MiB / 45.4% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 17,192
(144 MiB / 45.3% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 5,502
(125 MiB / 33.7% CPU)
🥇 19,828
(232 MiB / 44% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 36982444617). Lite = no-op handlers (unchanged-lite finish). Full = append-only header mutation (harness: one probe header each way; product: generic append-only relay via MitmCompressedRelayHelper). nginx/HAProxy/Envoy/YARP cannot MITM. Lite÷Reverse / Full÷Reverse vs bare reverse (same job / comparison-group shard). Completion gate: Lite ≥ 0.40× and Full ≥ 0.40× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.60× (no terminate-peer gate).

v1 append-only relay (2026-08-27): Pre-fix H2→H2 Full÷Reverse was 0.13–0.16× (32960766249). Post-fix @ df172718: H2 plain→H2 plain Full 0.77–0.79×, H3→H1 Full 0.91–0.93×, all MITM arms ≥ 0.70× on median of 33041445371, 33055267086, 33055272140.

v2 drop-only + non-unique append (2026-08-27): MitmStaticRebuildHelper rebuilds static HPACK/QPACK after 1–4 unique header drops; trailing non-unique appends stay on compressed relay. @ 41f4adee: all MITM arms ≥ 0.70× on GHA median (33087088466, 33087091622, 33105885748 Linux; 33087085235, 33087088466, 33087091622 Windows). H2 plain→H2 plain Full 0.77–0.79× (Win) / 0.78× (Lin).

Client Origin Lite sustain Full sustain Lite÷Reverse Full÷Reverse
HTTP/1 · plain HTTP/1 · plain 35,746
(112 MiB / 36.5% CPU)
24,534
(117 MiB / 34.6% CPU)
1.32× 0.91×
HTTP/1 · plain HTTP/1 · TLS 15,942
(131 MiB / 32.8% CPU)
20,982
(129 MiB / 32.4% CPU)
0.93× 1.22×
HTTP/1 · plain HTTP/2 · plain 34,723
(144 MiB / 36.3% CPU)
45,571
(148 MiB / 42% CPU)
1.06× 1.39×
HTTP/1 · plain HTTP/2 · TLS 30,884
(166 MiB / 38.8% CPU)
30,650
(159 MiB / 37.9% CPU)
0.92× 0.91×
HTTP/1 · plain HTTP/3 · QUIC 22,609
(143 MiB / 46.4% CPU)
18,784
(150 MiB / 45.1% CPU)
2.16× 1.79×
HTTP/1 · TLS HTTP/1 · plain 15,544
(124 MiB / 32.4% CPU)
16,418
(126 MiB / 31.7% CPU)
1.18× 1.25×
HTTP/1 · TLS HTTP/1 · TLS 12,869
(154 MiB / 31% CPU)
17,295
(151 MiB / 35.2% CPU)
0.78× 1.04×
HTTP/1 · TLS HTTP/2 · plain 21,932
(138 MiB / 41.4% CPU)
24,957
(146 MiB / 38.8% CPU)
1.7× 1.94×
HTTP/1 · TLS HTTP/2 · TLS 22,604
(204 MiB / 38.1% CPU)
17,668
(190 MiB / 35.4% CPU)
3.03× 2.37×
HTTP/1 · TLS HTTP/3 · QUIC 16,816
(157 MiB / 45.6% CPU)
12,059
(157 MiB / 41.3% CPU)
1.01× 0.73×
HTTP/2 · plain HTTP/1 · plain 36,969
(129 MiB / 41.5% CPU)
32,390
(126 MiB / 41.6% CPU)
0.82× 0.72×
HTTP/2 · plain HTTP/1 · TLS 20,368
(143 MiB / 42.5% CPU)
26,284
(140 MiB / 43.6% CPU)
0.67× 0.87×
HTTP/2 · plain HTTP/2 · plain 78,223
(118 MiB / 37.4% CPU)
73,211
(118 MiB / 35.7% CPU)
0.79× 0.74×
HTTP/2 · plain HTTP/2 · TLS 98,122
(121 MiB / 37.3% CPU)
81,877
(125 MiB / 37% CPU)
0.91× 0.76×
HTTP/2 · plain HTTP/3 · QUIC 17,556
(142 MiB / 48.1% CPU)
18,565
(143 MiB / 50.8% CPU)
1.19× 1.26×
HTTP/2 · TLS HTTP/1 · plain 34,535
(133 MiB / 40.3% CPU)
33,251
(135 MiB / 41.6% CPU)
0.96× 0.93×
HTTP/2 · TLS HTTP/1 · TLS 27,152
(138 MiB / 44% CPU)
37,855
(140 MiB / 45.4% CPU)
1.18× 1.65×
HTTP/2 · TLS HTTP/2 · plain 66,090
(128 MiB / 35.8% CPU)
60,172
(130 MiB / 34.3% CPU)
0.97× 0.88×
HTTP/2 · TLS HTTP/2 · TLS 70,582
(132 MiB / 37.8% CPU)
75,340
(132 MiB / 35.7% CPU)
0.74× 0.79×
HTTP/2 · TLS HTTP/3 · QUIC 17,434
(160 MiB / 49.2% CPU)
17,244
(157 MiB / 49% CPU)
0.87× 0.86×
HTTP/3 · QUIC HTTP/1 · plain 14,529
(139 MiB / 45.5% CPU)
12,468
(138 MiB / 42.3% CPU)
1.16× 1×
HTTP/3 · QUIC HTTP/1 · TLS 13,631
(143 MiB / 45.8% CPU)
11,639
(148 MiB / 45.7% CPU)
0.99× 0.85×
HTTP/3 · QUIC HTTP/2 · plain 31,499
(149 MiB / 48.5% CPU)
20,464
(146 MiB / 49% CPU)
1.52× 0.99×
HTTP/3 · QUIC HTTP/2 · TLS 18,549
(148 MiB / 48.6% CPU)
17,494
(146 MiB / 45.2% CPU)
0.89× 0.84×
HTTP/3 · QUIC HTTP/3 · QUIC 9,942
(143 MiB / 45.6% CPU)
12,760
(140 MiB / 45.5% CPU)
0.58× 0.74×

Editions (CLI / Plus / Intercept)

Note: twp-reverse-http1 and other library rows use Core with probe-tuned settings (no logging, no Via header, probe-warmed certs). Edition rows use titanium run -c twp.yaml product defaults — prefer the ÷baseline ratio column over absolute RPS. Inspector GUI is not spawnable in the harness; session-path overhead is twp-cli-intercept-http1 (route RequestHeaderSet transform). Pre-origin Plus middleware (CIDR/WAF/JWT/rate-limit) runs on H1 terminate-lite without SessionEventArgs; JWT caches successful bearer validations. Maintainer gate thresholds live under Maintainer notes.

Median of 3 repeats @ 41f4adee. Source: Actions 36853305087. Warmup 2s / measure 8s; concurrency 8–64; sustain = median peak RPS among SLO-pass steps @ c=64. RPS cells show sustain; <sub> holds peak (when higher) plus (MiB / CPU%).

Arm Win Linux Win÷ Lin÷ Gate
twp-cli-reverse-http1 vs library 66,323
(131 MiB / 45.5% CPU)
34,106
(166 MiB / 50.4% CPU)
1.03× 1.08× ≥ 0.80×
twp-cli-reverse-http1-tls vs library TLS 56,976
(145 MiB / 45.7% CPU)
24,583
(197 MiB / 50.3% CPU)
1.00× 1.04× ≥ 0.80×
twp-cli-reverse-http1-route vs CLI 66,646
(135 MiB / 47.6% CPU)
33,126
(169 MiB / 50.1% CPU)
1.00× 0.97× ≥ 0.90×
twp-cli-plus-base-http1 vs CLI 66,656
(137 MiB / 47.2% CPU)
34,197
(170 MiB / 50.5% CPU)
1.01× 1.00× ≥ 0.90×
twp-cli-plus-cache-http1 (cold) vs CLI 58,304
(140 MiB / 50.0% CPU)
28,354
(175 MiB / 54.0% CPU)
0.88× 0.83× ≥ 0.70×
twp-cli-intercept-http1 vs CLI 53,188
(138 MiB / 49.4% CPU)
24,664
(176 MiB / 52.9% CPU)
0.80× 0.72× ≥ 0.70×
twp-cli-plus-waf-http1 vs CLI 63,745
(134 MiB / 45.3% CPU)
32,738
(170 MiB / 51.2% CPU)
0.96× 0.96× ≥ 0.80×
twp-cli-plus-cidr-http1 vs CLI 64,199
(133 MiB / 47.6% CPU)
32,903
(170 MiB / 51.0% CPU)
0.97× 0.96× ≥ 0.80×
twp-cli-plus-jwt-http1 vs CLI 61,258
(149 MiB / 45.8% CPU)
30,232
(190 MiB / 50.7% CPU)
0.92× 0.89× ≥ 0.70×
twp-cli-plus-ratelimit-http1 vs CLI 63,961
(141 MiB / 47.0% CPU)
32,384
(170 MiB / 51.2% CPU)
0.96× 0.95× ≥ 0.80×
twp-cli-plus-resilience-http1 vs CLI 65,742
(144 MiB / 48.0% CPU)
33,765
(174 MiB / 50.6% CPU)
0.99× 0.99× ≥ 0.85×
twp-cli-plus-discovery-file-http1 vs CLI 65,441
(139 MiB / 47.0% CPU)
34,153
(172 MiB / 50.6% CPU)
0.99× 1.00× ≥ 0.80×
twp-cli-plus-metrics-scrape-http1 vs CLI 65,919
(140 MiB / 46.8% CPU)
33,848
(175 MiB / 50.3% CPU)
0.99× 0.99× ≥ 0.80×
twp-cli-plus-cache-hit-http1 vs cache cold 58,870
(147 MiB / 48.7% CPU)
28,214
(174 MiB / 53.4% CPU)
1.01× 1.00× ≥ 0.90×
twp-cli-static-http1 vs CLI 114,525
(130 MiB / 49.1% CPU)
54,684
(162 MiB / 50.2% CPU)
1.73× 1.60× ≥ 0.85×
twp-cli-logging-http1 vs CLI 67,217
(139 MiB / 45.5% CPU)
33,844
(163 MiB / 50.8% CPU)
1.01× 0.99× ≥ 0.90×
twp-cli-lb-leasttime-http1 vs route 63,630
(144 MiB / 50.9% CPU)
31,242
(191 MiB / 52.4% CPU)
0.95× 0.94× ≥ 0.85×
twp-cli-dialect-twp-http1 vs CLI 66,493
(134 MiB / 46.4% CPU)
33,965
(158 MiB / 50.3% CPU)
1.00× 1.00× ≥ 0.90×

validate-edition-gates.ps1 passed on both OS for this run. Library baselines @ c=64 (same job): Win H1 64191 / TLS 56711; Linux H1 31717 / TLS 23716. Laptop smoke ratios stay on Performance Local Lab — Editions.

Cross-version (7.0 vs 6.0)

Same reverse matrix measured on Titanium 7.0 versus committed 6.0 baselines (33087088466). Median of 3 @ be4d4e89. Source: Windows 36995934758, Linux 36995938517. Sustain RPS @ c=64. Absolute 7.0÷6.0 is 0.92–1.38× (Windows H3 is the high end). Peer-normalized TWP÷YARP is 0.94–1.17×.

Arm Win 6.0 Win 7.0 Win÷ Linux 6.0 Linux 7.0 Lin÷
twp-reverse-http1 32525 29929 0.92× 32755 31847 0.97×
twp-reverse-http1-tls 26461 25778 0.97× 22784 23663 1.04×
twp-reverse-http2 76038 87371 1.15× 47690 53242 1.12×
twp-reverse-http2-cleartext 40347 39304 0.97× 34556 34483 1.00×
twp-reverse-http3 17529 24213 1.38× 19468 20398 1.05×
twp-reverse-http3-cleartext 19956 19143 0.96× 20395 20825 1.02×
yarp-reverse-http1 (peer) 27504 26807 0.97× 27713 28228 1.02×
yarp-reverse-http2 (peer) 35553 34854 0.98× 29111 28944 0.99×

Both OS CSVs passed the cross-version check for this run. MITM arms are measured with the product reverse matrix, not this reverse-only comparison.

Heavier reverse workloads

Same runners and harness as the tiny-GET tables, but with larger bodies, POST, lossy links, TLS cost, and architecture-sensitive paths (slow consumer / early response / duplex). PUT with the same body is the same proxy work as POST; DELETE with no body matches GET — only POST is published. Bodies/POST/lossy stay half-duplex. Laptop numbers are on Performance Local Lab. How maintainers refresh these tables (modes, shards, paste scripts) is under Maintainer notes. Larger-body check: 64 / 256 KiB H2 TLS→H2 TLS is where body copy dominates headers — ratios vs YARP are in the tables below (@ 41f4adee), unlike the tiny-GET H2↔H2 medals above.

Lossy link = userspace delay/drop shim (not kernel netem): TCP gets per-buffer delay + occasional whole-connection stalls (honest head-of-line for multiplexed HTTP/2); UDP is loss% only (no per-datagram delay) + drops (QUIC / MsQuic-safe). Lossy tables publish HTTP/1, HTTP/2, and HTTP/3.

Windows — heavier reverse GET (64 KiB / 256 KiB)

Median of 3 repeats on windows-latest @ 41f4adee. Source: Actions 36853268072 (compare-bodies). Warmup 2s / measure 8s. RPS cells include (MiB / CPU%) footprints.

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Body Client Origin TWP nginx YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 9,134
(97 MiB / 44.1% CPU)
620
(141 MiB / 24.7% CPU)
6,985
(134 MiB / 41.5% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 11,535
(174 MiB / 46.7% CPU)
814
(141 MiB / 24.8% CPU)
9,117
(134 MiB / 49.1% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 3,937
(138 MiB / 37.4% CPU)
Not possible (no QUIC) 3,576
(196 MiB / 48.7% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 2,569
(123 MiB / 43.3% CPU)
167
(142 MiB / 24.8% CPU)
2,154
(129 MiB / 45.6% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 3,307
(152 MiB / 39.9% CPU)
197
(142 MiB / 24.7% CPU)
2,344
(134 MiB / 42.8% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 1,076
(109 MiB / 39.1% CPU)
Not possible (no QUIC) 🥇 1,086
(165 MiB / 45.0% CPU)
64 KiB HTTP/2 · plain HTTP/1 · plain 🥇 13,884
(175 MiB / 39.7% CPU)
3,252
(127 MiB / 24.8% CPU)
13,150
(111 MiB / 44.4% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 9,634
(114 MiB / 38.6% CPU)
Not possible 6,556
(138 MiB / 49.3% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 10,247
(120 MiB / 38.7% CPU)
Not possible 7,632
(131 MiB / 48.6% CPU)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 3,899
(124 MiB / 39.4% CPU)
Not possible 3,497
(187 MiB / 46.7% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 5,698
(143 MiB / 42.4% CPU)
Not possible (no QUIC) 4,442
(202 MiB / 48.0% CPU)
256 KiB HTTP/2 · plain HTTP/1 · plain 🥇 4,390
(145 MiB / 30.5% CPU)
978
(127 MiB / 24.5% CPU)
3,435
(123 MiB / 35.5% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 2,521
(142 MiB / 36.1% CPU)
Not possible 1,810
(167 MiB / 46.0% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 2,262
(139 MiB / 36.4% CPU)
Not possible 1,776
(148 MiB / 42.8% CPU)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 880
(149 MiB / 42.4% CPU)
Not possible 🥇 995
(198 MiB / 43.8% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,389
(151 MiB / 39.5% CPU)
Not possible (no QUIC) 1,205
(203 MiB / 46.2% CPU)

nginx/Windows collapses on large reverse bodies in this harness; treat as same-OS only. H1 TLS 64 KiB ≈ 1.13× YARP; 256 KiB ≈ 1.08×. H2→H1 64 KiB ≈ 1.17×; H3→H1 64 KiB ≈ 1.06×.

Linux — heavier reverse GET (64 KiB / 256 KiB)

Median of 3 repeats @ 41f4adee. Source: Actions 36853268072 (compare-bodies). Warmup 2s / measure 8s.

Body Client Origin TWP nginx HAProxy Envoy YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 8,426
(123 MiB / 44.4% CPU)
5,076
(100 MiB / 49.4% CPU)
🥇 8,950
(84 MiB / 40.0% CPU)
7,261
(131 MiB / 46.1% CPU)
6,410
(163 MiB / 48.9% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 13,272
(242 MiB / 39.0% CPU)
3,473
(102 MiB / 14.4% CPU)
11,621
(86 MiB / 24.2% CPU)
10,632
(141 MiB / 23.7% CPU)
11,543
(155 MiB / 44.1% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 5,644
(184 MiB / 44.8% CPU)
1,651
(peak 1,696 · 105 MiB / 22.4% CPU)
4,139
(89 MiB / 27.6% CPU)
0
(138 MiB / 0.2% CPU)
4,274
(225 MiB / 51.4% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 2,724
(126 MiB / 36.8% CPU)
1,731
(100 MiB / 53.5% CPU)
🥇 2,959
(83 MiB / 33.5% CPU)
2,697
(145 MiB / 33.9% CPU)
2,139
(168 MiB / 45.5% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 2,976
(223 MiB / 28.9% CPU)
1,038
(102 MiB / 14.1% CPU)
🥇 3,705
(83 MiB / 18.9% CPU)
3,680
(162 MiB / 19.6% CPU)
3,086
(159 MiB / 37.0% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 1,464
(157 MiB / 43.0% CPU)
448
(109 MiB / 23.9% CPU)
1,285
(89 MiB / 31.2% CPU)
805
(154 MiB / 20.2% CPU)
1,263
(217 MiB / 47.9% CPU)
64 KiB HTTP/2 · plain HTTP/1 · plain 🥇 19,850
(244 MiB / 41.2% CPU)
4,660
(80 MiB / 14.4% CPU)
14,246
(69 MiB / 24.2% CPU)
16,092
(130 MiB / 23.6% CPU)
16,776
(139 MiB / 40.5% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 5,497
(158 MiB / 34.4% CPU)
Not possible 2,086
(89 MiB / 24.6% CPU)
4,607
(144 MiB / 22.0% CPU)
4,582
(182 MiB / 46.7% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 9,858
(184 MiB / 38.2% CPU)
Not possible 4,172
(82 MiB / 24.6% CPU)
7,131
(146 MiB / 22.6% CPU)
8,403
(186 MiB / 43.4% CPU)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 3,737
(171 MiB / 45.1% CPU)
Not possible 2,193
(92 MiB / 35.0% CPU)
12
(146 MiB / 0.2% CPU)
3,051
(237 MiB / 48.6% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 8,245
(219 MiB / 41.4% CPU)
2,792
(109 MiB / 23.1% CPU)
7,599
(97 MiB / 34.2% CPU)
0
(141 MiB / 0.1% CPU)
7,119
(247 MiB / 48.1% CPU)
256 KiB HTTP/2 · plain HTTP/1 · plain 4,671
(221 MiB / 30.6% CPU)
1,537
(80 MiB / 15.7% CPU)
4,782
(70 MiB / 18.6% CPU)
🥇 4,868
(157 MiB / 20.2% CPU)
4,754
(156 MiB / 31.8% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 1,766
(176 MiB / 32.2% CPU)
Not possible 590
(91 MiB / 24.4% CPU)
1,578
(171 MiB / 21.5% CPU)
1,296
(191 MiB / 42.6% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 2,642
(169 MiB / 32.6% CPU)
Not possible 1,184
(88 MiB / 24.3% CPU)
1,995
(162 MiB / 22.5% CPU)
2,328
(185 MiB / 38.8% CPU)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 832
(188 MiB / 51.0% CPU)
Not possible 629
(94 MiB / 36.7% CPU)
651
(159 MiB / 22.3% CPU)
🥇 906
(225 MiB / 46.3% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 2,299
(192 MiB / 40.3% CPU)
719
(123 MiB / 23.7% CPU)
1,886
(96 MiB / 29.9% CPU)
1,235
(143 MiB / 20.2% CPU)
1,924
(238 MiB / 44.8% CPU)

On this GHA pass TWP÷YARP H1 TLS ≈ 1.21× (64 KiB) / 1.26× (256 KiB); H2→H1 ≈ 1.20× / 1.15×; H3→H1 ≈ 1.29× / 1.16×. TWP÷nginx H1 TLS ≈ 1.43 / 1.85. Absolute RPS swings by VM; prefer ratios.

macOS — heavier reverse GET (64 KiB / 256 KiB)

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 41f4adee. Source: Actions 36998170689 (compare-bodies). Warmup 2s / measure 8s.

Body Client Origin TWP nginx HAProxy Envoy YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 4,965
(181 MiB / 33.9% CPU)
6,239
(84 MiB / 25.0% CPU)
6,811
(101 MiB / 34.8% CPU)
🥇 6,927
(132 MiB / 29.8% CPU)
5,833
(183 MiB / 40.2% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 3,190
(432 MiB / 25.3% CPU)
5,651
(85 MiB / 24.4% CPU)
4,656
(103 MiB / 20.4% CPU)
3,817
(145 MiB / 18.1% CPU)
🥇 5,858
(169 MiB / 44.3% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 2,322
(281 MiB / 33.9% CPU)
0
(peak 2,645 · 106 MiB / 28.5% CPU)
2,009
(103 MiB / 29.8% CPU)
1,329
(138 MiB / 31.6% CPU)
2,001
(423 MiB / 32.9% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 2,900
(203 MiB / 30.8% CPU)
2,230
(85 MiB / 23.9% CPU)
2,706
(101 MiB / 30.5% CPU)
1,812
(154 MiB / 25.4% CPU)
1,823
(185 MiB / 35.0% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 1,620
(400 MiB / 35.5% CPU)
1,930
(85 MiB / 23.7% CPU)
🥇 2,510
(101 MiB / 23.9% CPU)
2,147
(155 MiB / 25.9% CPU)
1,477
(181 MiB / 43.1% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 592
(174 MiB / 30.8% CPU)
🥇 782
(peak 957 · 82 MiB / 28.2% CPU)
484
(100 MiB / 31.8% CPU)
277
(160 MiB / 35.6% CPU)
290
(469 MiB / 31.8% CPU)
64 KiB HTTP/2 · plain HTTP/1 · plain 4,044
(443 MiB / 15.8% CPU)
9,200
(73 MiB / 23.0% CPU)
10,775
(89 MiB / 23.9% CPU)
🥇 13,514
(135 MiB / 27.6% CPU)
11,451
(181 MiB / 37.0% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · plain 6,719
(304 MiB / 32.0% CPU)
Not possible 3,562
(104 MiB / 24.1% CPU)
6,814
(131 MiB / 27.9% CPU)
🥇 7,703
(244 MiB / 43.3% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 5,719
(209 MiB / 33.7% CPU)
Not possible 3,312
(101 MiB / 24.9% CPU)
🥇 5,834
(131 MiB / 28.5% CPU)
4,946
(236 MiB / 42.3% CPU)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 1,893
(238 MiB / 32.0% CPU)
Not possible 1,328
(103 MiB / 31.9% CPU)
700
(129 MiB / 39.1% CPU)
1,795
(432 MiB / 33.8% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 2,180
(247 MiB / 42.0% CPU)
0
(peak 1,559 · 112 MiB / 24.6% CPU)
1,788
(104 MiB / 34.2% CPU)
1,286
(139 MiB / 39.2% CPU)
1,291
(389 MiB / 40.4% CPU)
256 KiB HTTP/2 · plain HTTP/1 · plain 3,184
(370 MiB / 29.6% CPU)
3,310
(73 MiB / 19.6% CPU)
🥇 3,514
(87 MiB / 21.9% CPU)
3,199
(146 MiB / 24.7% CPU)
2,653
(192 MiB / 34.1% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 2,076
(705 MiB / 30.5% CPU)
Not possible 1,050
(112 MiB / 23.1% CPU)
2,021
(155 MiB / 27.1% CPU)
1,535
(330 MiB / 39.0% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 1,313
(449 MiB / 32.4% CPU)
Not possible 807
(102 MiB / 24.3% CPU)
1,311
(155 MiB / 26.9% CPU)
1,017
(304 MiB / 38.5% CPU)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 112
(peak 168 · 279 MiB / 25.3% CPU)
Not possible 310
(peak 370 · 103 MiB / 30.3% CPU)
164
(126 MiB / 36.6% CPU)
🥇 495
(484 MiB / 40.4% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 630
(218 MiB / 39.4% CPU)
575
(105 MiB / 28.2% CPU)
506
(104 MiB / 33.6% CPU)
249
(125 MiB / 31.5% CPU)
504
(423 MiB / 43.1% CPU)

Windows — POST 64 KiB request + 64 KiB response

Median of 3 repeats on windows-latest @ 41f4adee. Source: Actions 36853276459 (compare-post).

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Client Origin TWP nginx YARP
HTTP/1 · TLS HTTP/1 · plain 🥇 6,621
(96 MiB / 44.6% CPU)
382
(144 MiB / 24.7% CPU)
4,412
(137 MiB / 56.9% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 4,572
(178 MiB / 48.4% CPU)
375
(144 MiB / 24.8% CPU)
3,938
(135 MiB / 51.0% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 2,239
(175 MiB / 40.5% CPU)
Not possible (no QUIC) 2,223
(218 MiB / 49.3% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 6,806
(178 MiB / 46.5% CPU)
1,946
(130 MiB / 24.8% CPU)
6,321
(124 MiB / 51.1% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 5,685
(123 MiB / 38.1% CPU)
Not possible 3,894
(143 MiB / 48.9% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 4,157
(123 MiB / 38.9% CPU)
Not possible 3,060
(142 MiB / 47.3% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 2,301
(164 MiB / 43.3% CPU)
Not possible 2,018
(206 MiB / 48.5% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 2,104
(183 MiB / 41.8% CPU)
Not possible (no QUIC) 2,002
(215 MiB / 49.3% CPU)

TWP leads H1 POST (1.5x YARP on Windows and Linux), H2→H1 POST (1.1-1.3x YARP), and H3 POST (~1.0-1.1x YARP). H2 TLS→H2 TLS POST sustain is healthy on this pass (@ 41f4adee; TWP ~1.2-1.4x YARP).

Linux — POST 64 KiB request + 64 KiB response

Median of 3 repeats @ 41f4adee. Source: Actions 36853276459 (compare-post).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 4,742
(134 MiB / 45.2% CPU)
3,498
(100 MiB / 48.2% CPU)
🥇 5,044
(83 MiB / 41.4% CPU)
4,956
(132 MiB / 42.0% CPU)
3,176
(176 MiB / 55.5% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 3,056
(217 MiB / 47.3% CPU)
1,354
(112 MiB / 21.0% CPU)
1,818
(82 MiB / 23.9% CPU)
0
(peak 2,979 · 143 MiB / 23.9% CPU)
2,533
(168 MiB / 48.1% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 2,878
(220 MiB / 44.2% CPU)
463
(109 MiB / 24.9% CPU)
1,991
(90 MiB / 33.1% CPU)
0
(126 MiB / 0.1% CPU)
2,593
(243 MiB / 49.2% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 5,628
(228 MiB / 44.0% CPU)
2,396
(96 MiB / 24.1% CPU)
2,567
(68 MiB / 23.2% CPU)
4,030
(peak 4,604 · 118 MiB / 21.6% CPU)
4,340
(160 MiB / 47.3% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 3,268
(159 MiB / 41.6% CPU)
Not possible 1,340
(86 MiB / 24.0% CPU)
2,854
(142 MiB / 23.5% CPU)
2,389
(179 MiB / 46.1% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 2,330
(148 MiB / 40.5% CPU)
Not possible 1,010
(80 MiB / 23.1% CPU)
2,062
(146 MiB / 22.9% CPU)
1,903
(170 MiB / 46.3% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 2,132
(222 MiB / 43.9% CPU)
Not possible 1,136
(96 MiB / 31.5% CPU)
0
(126 MiB / 0.1% CPU)
1,800
(250 MiB / 47.3% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 2,139
(232 MiB / 44.8% CPU)
368
(120 MiB / 25.0% CPU)
1,669
(96 MiB / 36.1% CPU)
0
(125 MiB / 0.1% CPU)
1,973
(255 MiB / 48.6% CPU)

Linux nginx H1/H2/H3 POST completed (nginx.org mainline). TWP÷YARP H1 ≈ 1.5×; H2→H1 ≈ 1.2×; H3 ≈ 1.1×. TWP÷nginx H3 ≈ 6×.

macOS — POST 64 KiB request + 64 KiB response

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 41f4adee. Source: Actions 37006733365 (compare-post).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 2,944
(246 MiB / 28.8% CPU)
🥇 3,446
(89 MiB / 22.7% CPU)
3,383
(102 MiB / 32.7% CPU)
3,264
(132 MiB / 30.9% CPU)
1,832
(195 MiB / 35.5% CPU)
HTTP/2 · TLS HTTP/1 · plain 2,377
(419 MiB / 36.4% CPU)
🥇 2,705
(97 MiB / 21.8% CPU)
2,257
(101 MiB / 24.2% CPU)
0
(peak 2,840 · 146 MiB / 27.0% CPU)
2,266
(182 MiB / 41.1% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 918
(371 MiB / 31.7% CPU)
0
(peak 1,042 · 86 MiB / 26.5% CPU)
862
(101 MiB / 32.1% CPU)
311
(122 MiB / 38.2% CPU)
735
(447 MiB / 29.2% CPU)
HTTP/2 · plain HTTP/1 · plain 4,651
(388 MiB / 31.7% CPU)
🥇 4,975
(85 MiB / 21.3% CPU)
3,694
(86 MiB / 23.4% CPU)
0
(peak 4,705 · 135 MiB / 25.7% CPU)
3,661
(189 MiB / 41.0% CPU)
HTTP/2 · TLS HTTP/2 · plain 3,798
(317 MiB / 30.6% CPU)
Not possible 2,251
(102 MiB / 24.1% CPU)
🥇 4,595
(134 MiB / 26.9% CPU)
3,220
(203 MiB / 45.5% CPU)
HTTP/2 · TLS HTTP/2 · TLS 2,110
(245 MiB / 33.3% CPU)
Not possible 1,592
(103 MiB / 25.1% CPU)
🥇 2,156
(133 MiB / 26.5% CPU)
1,876
(209 MiB / 40.6% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 1,233
(263 MiB / 39.1% CPU)
Not possible 647
(103 MiB / 32.6% CPU)
429
(peak 477 · 129 MiB / 39.7% CPU)
925
(400 MiB / 41.4% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,035
(278 MiB / 39.5% CPU)
313
(peak 779 · 83 MiB / 12.8% CPU)
663
(99 MiB / 31.0% CPU)
309
(peak 409 · 130 MiB / 32.1% CPU)
828
(435 MiB / 37.6% CPU)

Windows — lossy / high-RTT (H2 HOL / H3 loss)

Userspace 5 ms one-way delay + 1% TCP connection stall (H1/H2); UDP is loss% only (no per-datagram delay; MsQuic-safe) + 1% datagram drop (H3); 64 KiB GET. Median of 3 repeats on windows-latest @ 41f4adee — 36853280515 (compare-lossy).

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Client Origin TWP nginx YARP
HTTP/1 · TLS HTTP/1 · plain 🥇 663
(88 MiB / 3.9% CPU)
650
(142 MiB / 17.5% CPU)
662
(118 MiB / 5.0% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 59
(peak 85 · 112 MiB / 1.6% CPU)
18
(142 MiB / 0.6% CPU)
18
(86 MiB / 0.9% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 1,123
(124 MiB / 16.9% CPU)
Not possible (no QUIC) 969
(188 MiB / 22.0% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 61
(peak 87 · 106 MiB / 1.6% CPU)
18
(127 MiB / 0.1% CPU)
17
(78 MiB / 0.9% CPU)
HTTP/2 · TLS HTTP/2 · plain 16
(79 MiB / 0.9% CPU)
Not possible 🥇 18
(85 MiB / 1.4% CPU)
HTTP/2 · TLS HTTP/2 · TLS 16
(78 MiB / 0.7% CPU)
Not possible 🥇 18
(84 MiB / 1.4% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 1,042
(121 MiB / 16.1% CPU)
Not possible 🥇 1,244
(192 MiB / 29.5% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 998
(132 MiB / 16.4% CPU)
Not possible (no QUIC) 🥇 1,217
(185 MiB / 30.3% CPU)

H1 is near parity with YARP. H2 HOL and H3 loss sustain are non-zero on this Windows GHA pass (@ 41f4adee); see table.

Linux — lossy / high-RTT (H2 HOL / H3 loss)

Median of 3 repeats @ 41f4adee. Source: 36853280515 (compare-lossy; lossy H3 uses quic-http3, UDP drop-only).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 1,214
(120 MiB / 10.1% CPU)
🥇 1,220
(102 MiB / 8.0% CPU)
1,219
(83 MiB / 4.8% CPU)
1,205
(128 MiB / 6.4% CPU)
1,209
(148 MiB / 12.9% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 315
(177 MiB / 5.2% CPU)
40
(101 MiB / 0.2% CPU)
40
(84 MiB / 0.2% CPU)
40
(137 MiB / 0.3% CPU)
40
(127 MiB / 1.1% CPU)
HTTP/3 · QUIC HTTP/1 · plain 1,516
(165 MiB / 21.2% CPU)
🥇 1,570
(117 MiB / 23.2% CPU)
1,338
(87 MiB / 18.7% CPU)
1,462
(138 MiB / 24.5% CPU)
1,510
(228 MiB / 30.5% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 329
(185 MiB / 5.3% CPU)
40
(78 MiB / 0.2% CPU)
41
(69 MiB / 0.1% CPU)
40
(127 MiB / 0.3% CPU)
40
(123 MiB / 0.9% CPU)
HTTP/2 · TLS HTTP/2 · plain 40
(111 MiB / 0.7% CPU)
Not possible 40
(85 MiB / 0.3% CPU)
40
(135 MiB / 0.3% CPU)
🥇 41
(130 MiB / 1.3% CPU)
HTTP/2 · TLS HTTP/2 · TLS 40
(108 MiB / 0.9% CPU)
Not possible 🥇 42
(87 MiB / 0.3% CPU)
40
(137 MiB / 0.3% CPU)
41
(130 MiB / 1.2% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 1,477
(163 MiB / 23.1% CPU)
Not possible 1,212
(93 MiB / 21.4% CPU)
1,379
(140 MiB / 25.8% CPU)
1,417
(235 MiB / 31.4% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 1,351
(169 MiB / 21.9% CPU)
🥇 1,452
(120 MiB / 23.7% CPU)
1,289
(92 MiB / 20.8% CPU)
1,404
(138 MiB / 25.9% CPU)
1,384
(213 MiB / 30.8% CPU)

TWP H2 HOL ≫ YARP (~7.8×). H3 TWP÷YARP ≈ 1.07×.

macOS — lossy / high-RTT (H2 HOL / H3 loss)

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 41f4adee. Source: 37007354328 (compare-lossy; lossy H3 uses quic-http3, UDP drop-only).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 502
(261 MiB / 7.8% CPU)
🥇 533
(86 MiB / 3.5% CPU)
497
(101 MiB / 2.7% CPU)
450
(131 MiB / 3.2% CPU)
431
(208 MiB / 5.5% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 24
(peak 45 · 140 MiB / 3.6% CPU)
12
(81 MiB / 0.2% CPU)
10
(96 MiB / 0.2% CPU)
8
(118 MiB / 0.3% CPU)
11
(130 MiB / 3.3% CPU)
HTTP/3 · QUIC HTTP/1 · plain 1,103
(279 MiB / 25.6% CPU)
🥇 1,532
(98 MiB / 15.9% CPU)
673
(101 MiB / 21.7% CPU)
1,023
(131 MiB / 29.9% CPU)
557
(429 MiB / 21.6% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 19
(peak 46 · 119 MiB / 2.4% CPU)
12
(69 MiB / 0.1% CPU)
12
(83 MiB / 0.2% CPU)
10
(108 MiB / 0.3% CPU)
12
(117 MiB / 2.5% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 12
(114 MiB / 2.2% CPU)
Not possible 11
(97 MiB / 0.3% CPU)
8
(118 MiB / 0.3% CPU)
11
(128 MiB / 3.2% CPU)
HTTP/2 · TLS HTTP/2 · TLS 10
(125 MiB / 2.3% CPU)
Not possible 10
(peak 12 · 98 MiB / 0.4% CPU)
8
(118 MiB / 0.3% CPU)
🥇 11
(131 MiB / 3.6% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 906
(194 MiB / 24.3% CPU)
Not possible 840
(105 MiB / 22.4% CPU)
🥇 1,154
(131 MiB / 31.1% CPU)
1,016
(444 MiB / 31.0% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 1,346
(195 MiB / 27.6% CPU)
🥇 1,437
(101 MiB / 18.0% CPU)
872
(106 MiB / 22.9% CPU)
946
(131 MiB / 35.0% CPU)
878
(389 MiB / 31.5% CPU)

Architecture-sensitive

These runs isolate slow app readers, origin-early response, HTTP/2 duplex, and WebSocket echo. See TWP vs YARP IO model. Laptop 1-rep numbers are on Performance Local Lab.

Median of 3 repeats on matched 4 vCPU / 16 GiB runners @ 41f4adee (36853288521). Slow consumer = 256 KiB GET, 16 KiB read + 8 ms sleep. Early response = 64 KiB POST, origin writes after 8 KiB. Duplex HTTP/2 = overlapping 64 KiB POST on H2 TLS↔H2 TLS. WebSocket row = H1 Upgrade echo round-trips/sec (not RFC 8441; see WebSocket RFC 8441 table below).

Lossy-link runs (slow network) are already published above; they are not a slow app reader.

Windows

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Scenario Client Origin TWP nginx YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 🥇 248
(91 MiB / 5.3% CPU)
220
(144 MiB / 24.7% CPU)
241
(111 MiB / 5.3% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 256
(118 MiB / 3.6% CPU)
230
(141 MiB / 24.4% CPU)
🥇 256
(114 MiB / 5.3% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 282
(104 MiB / 12.3% CPU)
Not possible (no QUIC) 276
(173 MiB / 14.6% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 🥇 13,378
(98 MiB / 42.5% CPU)
723
(142 MiB / 24.9% CPU)
7,476
(137 MiB / 50.0% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 🥇 3,367
(202 MiB / 52.7% CPU)
350
(143 MiB / 24.7% CPU)
2,711
(136 MiB / 49.8% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 2,777
(153 MiB / 42.6% CPU)
Not possible (no QUIC) 2,477
(213 MiB / 52.8% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 248
(108 MiB / 3.2% CPU)
250
(127 MiB / 9.2% CPU)
🥇 256
(105 MiB / 4.1% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 🥇 256
(123 MiB / 3.5% CPU)
Not possible 256
(142 MiB / 6.1% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 3,606
(118 MiB / 30.4% CPU)
Not possible 21
(103 MiB / 0.3% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 3,531
(119 MiB / 29.9% CPU)
Not possible 15
(109 MiB / 0.3% CPU)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 🥇 69,940
(98 MiB / 44.0% CPU)
38,586
(143 MiB / 24.6% CPU)
65,385
(90 MiB / 45.1% CPU)

Linux

Scenario Client Origin TWP nginx HAProxy Envoy YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 463
(119 MiB / 9.7% CPU)
404
(100 MiB / 9.7% CPU)
466
(84 MiB / 5.9% CPU)
🥇 472
(136 MiB / 6.4% CPU)
417
(146 MiB / 14.6% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 🥇 474
(137 MiB / 19.9% CPU)
468
(100 MiB / 23.1% CPU)
473
(84 MiB / 9.3% CPU)
468
(154 MiB / 7.5% CPU)
466
(144 MiB / 23.9% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 473
(130 MiB / 34.8% CPU)
120
(peak 342 · 94 MiB / 5.8% CPU)
472
(88 MiB / 10.5% CPU)
1
(144 MiB / 0.2% CPU)
472
(195 MiB / 37.8% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 5,001
(140 MiB / 45.5% CPU)
0
(peak 3,541 · 102 MiB / 34.1% CPU)
🥇 5,150
(83 MiB / 41.6% CPU)
0
(peak 2,904 · 129 MiB / 25.5% CPU)
3,344
(177 MiB / 55.4% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 🥇 2,430
(218 MiB / 48.5% CPU)
0
(peak 1,345 · 112 MiB / 24.1% CPU)
1,363
(84 MiB / 12.8% CPU)
0
(peak 2,354 · 153 MiB / 21.4% CPU)
2,167
(168 MiB / 48.6% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 2,939
(193 MiB / 45.7% CPU)
0
(peak 442 · 116 MiB / 24.8% CPU)
1,956
(91 MiB / 34.3% CPU)
0
(126 MiB / 0.1% CPU)
2,141
(253 MiB / 48.4% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 478
(142 MiB / 16.7% CPU)
463
(79 MiB / 11.7% CPU)
🥇 480
(68 MiB / 6.7% CPU)
474
(146 MiB / 4.7% CPU)
468
(147 MiB / 15.5% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 452
(144 MiB / 21.1% CPU)
Not possible 461
(88 MiB / 19.4% CPU)
460
(152 MiB / 12.4% CPU)
🥇 470
(162 MiB / 29.9% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 1,936
(peak 2,226 · 141 MiB / 35.4% CPU)
Not possible 1,147
(84 MiB / 23.2% CPU)
0
(peak 2,192 · 151 MiB / 21.8% CPU)
50
(145 MiB / 1.4% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 1,934
(141 MiB / 35.7% CPU)
Not possible 256
(86 MiB / 4.8% CPU)
0
(peak 2,083 · 156 MiB / 21.4% CPU)
6
(150 MiB / 0.4% CPU)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 34,436
(124 MiB / 44.9% CPU)
37,247
(100 MiB / 36.4% CPU)
🥇 37,608
(83 MiB / 39.8% CPU)
36,405
(127 MiB / 41.2% CPU)
31,719
(125 MiB / 44.9% CPU)

macOS

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 41f4adee. Source: Actions 37000022693 (compare-arch).

Scenario Client Origin TWP nginx HAProxy Envoy YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 113
(266 MiB / 3.5% CPU)
🥇 116
(85 MiB / 2.1% CPU)
112
(101 MiB / 2.2% CPU)
104
(145 MiB / 2.8% CPU)
111
(222 MiB / 4.0% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 103
(311 MiB / 5.5% CPU)
95
(84 MiB / 1.3% CPU)
🥇 120
(100 MiB / 1.9% CPU)
88
(155 MiB / 2.0% CPU)
112
(215 MiB / 5.5% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 155
(135 MiB / 18.8% CPU)
116
(94 MiB / 6.6% CPU)
113
(102 MiB / 10.0% CPU)
154
(159 MiB / 33.9% CPU)
144
(366 MiB / 20.1% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 3,869
(228 MiB / 33.6% CPU)
0
(peak 4,499 · 89 MiB / 25.2% CPU)
🥇 5,508
(102 MiB / 35.3% CPU)
4,603
(130 MiB / 36.9% CPU)
3,471
(195 MiB / 45.9% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 1,324
(443 MiB / 27.6% CPU)
0
(peak 3,690 · 98 MiB / 22.5% CPU)
🥇 3,214
(103 MiB / 25.1% CPU)
0
(peak 2,363 · 147 MiB / 25.7% CPU)
2,337
(179 MiB / 42.3% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 1,181
(277 MiB / 39.3% CPU)
0
(peak 1,423 · 112 MiB / 26.5% CPU)
0
(66 MiB)
390
(133 MiB / 34.0% CPU)
978
(450 MiB / 38.5% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 112
(185 MiB / 5.6% CPU)
104
(72 MiB / 1.1% CPU)
🥇 112
(86 MiB / 1.9% CPU)
89
(145 MiB / 1.7% CPU)
96
(145 MiB / 2.8% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 128
(306 MiB / 5.9% CPU)
Not possible 🥇 139
(105 MiB / 5.8% CPU)
104
(144 MiB / 3.8% CPU)
122
(286 MiB / 4.5% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 1,925
(183 MiB / 27.5% CPU)
Not possible 0
(98 MiB)
0
(peak 2,622 · 146 MiB / 25.5% CPU)
125
(181 MiB / 1.3% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 1,922
(peak 2,353 · 179 MiB / 29.7% CPU)
Not possible 1,517
(98 MiB / 22.9% CPU)
0
(peak 1,960 · 119 MiB / 14.0% CPU)
5
(173 MiB / 0.1% CPU)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 31,243
(163 MiB / 32.6% CPU)
25,995
(87 MiB / 18.8% CPU)
32,045
(100 MiB / 28.2% CPU)
🥇 38,061
(122 MiB / 33.2% CPU)
31,518
(188 MiB / 33.7% CPU)

Slow consumer is sleep-bound; H1/H2/H3 sit in the same band. Early-response H1: TWP leads (~1.79x / 1.50x YARP Win/Linux). Duplex H2 sustain is non-zero for TWP on this GHA pass (@ 41f4adee). WebSocket: TWP is ~1.07x YARP on Windows; on Linux HAProxy and nginx lead and TWP is ~1.09x YARP.

TLS termination cost (H1 TLS → cleartext origin)

Isolates keep-alive tiny GET vs new connection per request (handshake-dominated) vs keep-alive 256 KiB. Product comparison uses RPS and end-to-end latency; TWP can also capture ClientTlsTiming when TWP_RPS_CAPTURE_TLS=1 (child process) — nginx/YARP have no equivalent hook.

Windows

Median of 3 repeats on windows-latest @ 41f4adee. Source: Actions 36853284423. Absolute RPS on GHA swings hard; prefer TWP÷YARP.

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Workload TWP nginx YARP
Keep-alive · tiny GET 🥇 20,894
(84 MiB / 48.0% CPU)
8,845
(142 MiB / 24.6% CPU)
17,746
(101 MiB / 47.9% CPU)
New-connection · tiny GET 🥇 724
(80 MiB / 1.5% CPU)
248
(141 MiB / 1.4% CPU)
717
(112 MiB / 0.5% CPU)
Keep-alive · 256 KiB GET 🥇 2,843
(117 MiB / 47.5% CPU)
170
(142 MiB / 24.6% CPU)
2,663
(129 MiB / 47.1% CPU)

Linux

Median of 3 repeats @ 41f4adee. Source: Actions 36853284423.

Workload TWP nginx HAProxy Envoy YARP
Keep-alive · tiny GET 25,285
(107 MiB / 50.0% CPU)
🥇 29,752
(99 MiB / 42.2% CPU)
29,386
(84 MiB / 43.6% CPU)
18,088
(127 MiB / 58.7% CPU)
21,832
(134 MiB / 51.1% CPU)
New-connection · tiny GET 1,028
(117 MiB)
1,080
(101 MiB / 0.7% CPU)
1,014
(82 MiB / 0.2% CPU)
🥇 1,155
(128 MiB)
1,030
(147 MiB)
Keep-alive · 256 KiB GET 2,886
(126 MiB / 37.0% CPU)
1,817
(99 MiB / 53.2% CPU)
🥇 3,135
(83 MiB / 33.6% CPU)
2,879
(145 MiB / 33.9% CPU)
2,252
(174 MiB / 45.2% CPU)

macOS

Median of 3 repeats on macos-15 @ 41f4adee. Source: Actions 37008082576.

Workload TWP nginx HAProxy Envoy YARP
Keep-alive · tiny GET 15,772
(130 MiB / 23.8% CPU)
🥇 24,097
(84 MiB / 20.7% CPU)
20,381
(101 MiB / 21.1% CPU)
14,436
(121 MiB / 31.3% CPU)
4,022
(179 MiB / 8.1% CPU)
New-connection · tiny GET 106
(106 MiB / 0.1% CPU)
597
(86 MiB)
🥇 680
(101 MiB)
625
(131 MiB / 0.1% CPU)
108
(154 MiB / 0.2% CPU)
Keep-alive · 256 KiB GET 2,259
(213 MiB / 24.0% CPU)
2,003
(84 MiB / 19.1% CPU)
🥇 2,619
(101 MiB / 29.3% CPU)
2,341
(155 MiB / 25.2% CPU)
1,664
(165 MiB / 34.8% CPU)

On Windows, TWP leads YARP on keep-alive tiny (1.18×) and keep-alive 256 KiB (1.07×); new-connection is a tie (1.01×). On Linux, nginx leads keep-alive tiny (near-tie with HAProxy), Envoy leads new-connection, and HAProxy leads keep-alive 256 KiB; TWP stays ahead of YARP on keep-alive (1.16× tiny, ~1.28× 256 KiB) and ties on new-connection. On macOS, nginx leads keep-alive tiny, and HAProxy leads new-connection and keep-alive 256 KiB; TWP is ~3.9× YARP on keep-alive tiny and ~1.36× on 256 KiB. New-connection is Darwin SslStream-bound for TWP and YARP (handshake p99 SLO 500 ms on macOS only — Win/Linux stay at 200 ms).

Unary gRPC (H2 TLS)

Unary Echo RPC/s @ c=64 over H2 TLS→H2 TLS for Titanium, YARP, nginx (grpc_pass), HAProxy, and Envoy (OS-possible peers only). Not folded into the architecture-sensitive tables. Median of 3 repeats @ 41f4adee — 36853254836.

OS Titanium YARP nginx HAProxy Envoy
Windows 🥇 52,009
(96 MiB / 22.7% CPU)
31,057
(126 MiB / 45.5% CPU)
0
(143 MiB / 24.9% CPU)
Not possible Not possible
Linux 🥇 41,339
(117 MiB / 30.4% CPU)
24,277
(158 MiB / 41.9% CPU)
0
(114 MiB / 24.8% CPU)
8,952
(83 MiB / 24.4% CPU)
14,753
(127 MiB / 20.7% CPU)
macOS 28,466
(152 MiB / 11.5% CPU)
🥇 29,087
(189 MiB / 21.3% CPU)
0
(85 MiB / 2.4% CPU)
18,511
(104 MiB / 19.6% CPU)
16,457
(120 MiB / 16.1% CPU)

Unary gRPC (H2 TLS → h2c)

Unary Echo RPC/s @ c=64 over H2 TLS → h2c (edge TLS, cleartext H2 origin). Peers: Titanium, YARP, HAProxy, Envoy. Mode: compare-grpc (*-grpc-h2c). Median of 3 repeats @ 41f4adee — 36853254836.

Not possible: nginx column omitted (no H2 upstream).

OS Titanium YARP HAProxy Envoy
Windows 🥇 61,643
(90 MiB / 23.5% CPU)
36,011
(116 MiB / 48.1% CPU)
Not possible Not possible
Linux 🥇 43,166
(121 MiB / 29.6% CPU)
24,252
(161 MiB / 43.4% CPU)
7,782
(83 MiB / 24.5% CPU)
12,044
(127 MiB / 22.2% CPU)
macOS 🥇 22,187
(158 MiB / 9.5% CPU)
6,717
(193 MiB / 16.0% CPU)
7,750
(100 MiB / 13.9% CPU)
16,573
(119 MiB / 19.7% CPU)

WebSocket (H1 TLS → H1 TLS)

WebSocket echo round-trips/sec over dual-TLS H1 (proxy_ssl style). Mode: compare-ws-h1tls (*-duplex-ws-h1tls).

OS Titanium YARP nginx HAProxy Envoy
Windows 🥇 22,332
(101 MiB / 47.4% CPU)
21,366
(93 MiB / 43.1% CPU)
9,361
(142 MiB / 24.6% CPU)
Not possible Not possible
Linux 25,321
(141 MiB / 43.7% CPU)
22,634
(134 MiB / 44.1% CPU)
26,985
(103 MiB / 37.3% CPU)
26,853
(83 MiB / 39.7% CPU)
🥇 28,050
(127 MiB / 37.9% CPU)
macOS 13,710
(168 MiB / 18.8% CPU)
6,452
(148 MiB / 14.1% CPU)
9,309
(86 MiB / 16.6% CPU)
🥇 14,432
(100 MiB / 20.5% CPU)
11,461
(119 MiB / 20.6% CPU)

WebSocket (H2 TLS 8441 → H1)

WebSocket echo over RFC 8441 extended CONNECT (H2 TLS client → H1 plain origin). Mode: compare-ws-h2 (*-duplex-ws-h2).

The probe client sets NoDelay and writes each HTTP/2 frame as one TLS record. The previous Linux row (~1.5k at ~9% CPU, tied with HAProxy) was that client with Nagle on and the frame split across two TLS records, which waited out the ~40 ms delayed ACK. Median of 3 repeats @ f2061c27. Source: Linux 37004979850, Windows 37004984433, macOS 37004988043. macOS Titanium samples at c=64 were 6.1k / 7.7k / 19.4k; the cell is that median, not a tight result.

Not possible: nginx column omitted (no RFC 8441 extended CONNECT).

OS Titanium YARP HAProxy Envoy
Windows 🥇 28,294
(154 MiB / 43.7% CPU)
0
(152 MiB / 19.4% CPU)
Not possible Not possible
Linux 🥇 42,050
(200 MiB / 49.6% CPU)
0
(198 MiB / 43.0% CPU)
38,112
(83 MiB / 40.3% CPU)
0
(124 MiB / 0.3% CPU)
macOS 7,705
(288 MiB / 15.9% CPU)
0
(186 MiB / 9.8% CPU)
🥇 9,015
(101 MiB / 15.4% CPU)
0
(116 MiB / 0.4% CPU)

Other measurements

What Result
HTTPS TTFB vs direct (median, 14 hosts) Cold ≈ parity (−1 ms); warm −25 ms (proxy faster)
HTTP/1 loopback GET (no body intercept) ~128 µs, ~9.9 KB allocated / request
Basic example footprint (Release, after load) ~74 MB working set · ~24–29 MB private bytes
dotnet run -c Release --project benchmarks/Titanium.Web.Proxy.Benchmarks -- --filter '*Throughput*'

Local laptop BDN @ 9d7c2966 (Release):

Benchmark Setup Mean Allocated / op
HTTP/1 GET through proxy Passthrough 128 µs 9.9 KB
HTTP/2 multiplexed GETs 10 concurrent streams 253 µs / batch ~4.4 KB / request

Raising limits on large hosts

There is no artificial upper clamp on server defaults. Per-endpoint overrides:

Knob Scope Default Override
ProxyServer.MaxCachedConnections process, per upstream host 128 any ≥ 1
ProxyEndPoint.MaxCachedConnections endpoint → pool depth for that EP’s sessions null (use server) e.g. 256 on reverse EP
ProxyEndPoint.MaxConcurrentClients endpoint admission null (off) any ≥ 1
ResourceLimits.MaxConcurrentStreamsPerConnection H2 streams 256 ProxyResourceLimits.Create(...)
TransparentQuicProxyEndPoint.MaxInboundBidirectionalStreams H3 100 (probe uses 256) property on EP
ForwardCleartext transparent TLS terminate false true + decrypt
proxy.MaxCachedConnections = 512;
proxy.ResourceLimits = ProxyResourceLimits.Create(
    /* … */,
    maxConcurrentStreamsPerConnection: 1000,
    maxCachedConnectionsPerHost: 512,
    /* … */);

var ep = new TransparentProxyEndPoint(IPAddress.Any, 443, decryptSsl: true)
{
    ForwardHost = "127.0.0.1",
    ForwardPort = 8080,
    ForwardCleartext = true,
    MaxCachedConnections = 256,
    GenericCertificateName = "example.com"
};
ep.BeforeSslAuthenticate += (_, a) =>
{
    a.UpstreamHttpProtocol = UpstreamHttpProtocol.Http11;
    a.AllowHttpProtocolTranslation = true; // HTTP/2 client → HTTP/1 origin
    return Task.CompletedTask;
};

Maintainer notes

CI cadence, shards, paste scripts, and gate floors for people refreshing these tables. Consumers can skip this section.

Tiered cadence

Tier Mode When
Daily / per-PR compare-spot minutes
Milestone compare-terminate / compare-matrix ~1–2h
Editions compare-editions ~60 min (CLI / Plus / Intercept stress arms)
Cross-version (Gate 2) compare-cross-version ~1–2h vs committed 6.0 baselines
Pre-wiki smoke compare-product-smoke (Linux 2 shards, repeats=1) ~30–60 min; required before full product
Release / wiki compare-product (3 comparison-group shards × Win/Linux/mac) ~2–2½h wall (Free account queues beyond 20 jobs)
Unary gRPC compare-grpc Win/Linux/mac; H2↔H2 + H2→h2c (arm_shard 1/2, 2/2)
WebSocket dual-TLS compare-ws-h1tls Win/Linux/mac; H1 TLS→H1 TLS echo
WebSocket RFC 8441 compare-ws-h2 Win/Linux/mac; H2 TLS→H1 plain (nginx N/A)
Heavier tables compare-bodies (2 shards) / post / lossy / arch (3 shards) / tls-cost dispatch independently

See PERF-GATES.md.

Arm shards (comparison groups)

--arm-shard i/n (workflow input arm_shard) partitions wiki rows (Client×Origin + heavier/arch workload suffix), not individual proxy arms. Paste unions shard CSVs (paste-compare-product-wiki.ps1 -RunIds …; heavier RUNS lists). Table “Source: Actions” may list several run URLs for one table — do not mix SHAs. Free GitHub accounts allow 20 concurrent jobs (~34 for a full suite); extras queue, they do not fail. Dispatch product (and cross-version) first when wall clock matters.

One wiki row = one GHA job’s Client×Origin cell set: TWP + YARP + nginx + HAProxy + Envoy (when the OS can run them) + TWP Lite + Full stay on the same VM. Shards split rows, not individual proxies — so TWP÷YARP and Lite÷Reverse remain same-job ratios. Do not compare absolute RPS across shards (different VMs).

Gate thresholds

  • Reverse product signal: TWP÷YARP ≥ 0.75 (nginx / HAProxy / Envoy are wiki and chart peers only — no CI gate). Edition CLI/Plus ratios floor at 0.50.
  • MITM overhead: Lite÷Reverse ≥ 0.40 and Full÷Reverse ≥ 0.40 (median of 3 GHA runs @ c=64). Absolute RPS moves with runner heat; ratios are the claim.
  • Do not enable the HTTP/2 MITM multi-origin relay pool (MaxOriginHttp2ConnectionsPerAuthority > 1 under interception): Tip A/B showed Lite err%~29 and RSS blow-up, which fails the Lite/Full÷Reverse floors. Multi-origin remains gate-off compressed-relay only.
  • Editions: see PERF-GATES.md and validate-edition-gates.ps1.
  • Cross-version (Gate 2) before a major tag: run compare-cross-version (reverse matrix, routes unset) on develop vs committed 6.0 baselines (baseline-6.0-win.csv / baseline-6.0-linux.csv).
Gate Threshold
Peer-normalized RPS (when YARP peer exists) (TWP÷YARP)_7 ÷ (TWP÷YARP)_6 ≥ 0.90 or current (TWP÷YARP) ≥ 0.90
Absolute RPS floor (TWP arms) 7.0 ÷ 6.0 ≥ 0.70 (runner heat — peers move with the box)
RSS 7.0 ÷ 6.0 ≤ 1.20 per TWP arm @ c=64

nginx/YARP absolute RPS is not gated. See validate-cross-version.ps1.

pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-cross-version
pwsh tools/RpsLoadProbe/validate-cross-version.ps1 `
  -BaselineCsv tools/RpsLoadProbe/results/baseline-6.0-win.csv `
  -CurrentCsv  tools/RpsLoadProbe/results/rps-ramp-*.csv

Paste / regenerate

Product refresh and heavier tables:

pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-product
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-editions
pwsh tools/RpsLoadProbe/validate-edition-gates.ps1 -CsvPath tools/RpsLoadProbe/results/rps-ramp-*.csv
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-bodies
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-post
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-lossy
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-tls-cost
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-arch
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-saturation

Harness details: RpsLoadProbe. The harness does not disable TWP safety that peers also skip, and it does not retune to pass gates (PERF-GATES.md). MITM leaf keys are a shared RSA/ECDSA pair per CertificateManager (intentional RPS tradeoff).

Clone this wiki locally