Skip to content
github-actions[bot] edited this page Oct 4, 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. The published MITM table is Linux only. It shows 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 (@ 0386b2aa).

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 is published on Linux only (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. Median of 3 repeats @ 0386b2aa — 37156912437. 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 81,424
(55 MiB / 44.9% CPU)
100.0%
origin-direct-bombardier bombardier 62,541
(56 MiB / 24.2% CPU)
76.8%
bare-reverse-http1 dotnet-httpclient 40,689
(58 MiB / 45.4% CPU)
50.0%
nginx-reverse-http1 dotnet-httpclient 25,102
(126 MiB / 24.7% CPU)
30.8%
yarp-reverse-http1 dotnet-httpclient 35,822
(88 MiB / 50.3% CPU)
44.0%
twp-reverse-http1 dotnet-httpclient 🥇 42,740
(71 MiB / 48.0% CPU)
52.5%

Linux (ubuntu-latest)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 69,231
(79 MiB / 42.2% CPU)
100.0%
origin-direct-bombardier bombardier 41,477
(80 MiB / 35.1% CPU)
59.9%
bare-reverse-http1 dotnet-httpclient 32,207
(72 MiB / 45.1% CPU)
46.5%
nginx-reverse-http1 dotnet-httpclient 🥇 38,745
(76 MiB / 41.1% CPU)
56.0%
yarp-reverse-http1 dotnet-httpclient 27,681
(114 MiB / 50.5% CPU)
40.0%
twp-reverse-http1 dotnet-httpclient 32,368
(85 MiB / 49.3% CPU)
46.8%

macOS (macos-15, Apple Silicon)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 52,539
(89 MiB / 31.7% CPU)
100.0%
origin-direct-bombardier bombardier 38,063
(89 MiB / 22.4% CPU)
72.4%
bare-reverse-http1 dotnet-httpclient 20,688
(91 MiB / 27.7% CPU)
39.4%
nginx-reverse-http1 dotnet-httpclient 🥇 26,334
(68 MiB / 20.6% CPU)
50.1%
yarp-reverse-http1 dotnet-httpclient 22,962
(147 MiB / 36.7% CPU)
43.7%
twp-reverse-http1 dotnet-httpclient 18,985
(114 MiB / 30.5% CPU)
36.1%

On this macOS run origin-direct was 31.7% CPU, so the % of origin-HttpClient column is not a ceiling when that CPU is near idle. Rank those Mac peers by RPS.

Reverse peers on Block A @ 0386b2aa: Windows TWP is 52.5% of origin-direct and Linux TWP is 46.8%. 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 14,643
(142 MiB / 24.4% CPU)
0.31× 1.00×
yarp-reverse-http2 dotnet-httpclient 46,665
(92 MiB / 52.2% CPU)
1.00× 3.19×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 54,734
(103 MiB / 50.0% CPU)
1.17× 3.74×

Linux (ubuntu-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 15,027
(100 MiB / 19.4% CPU)
0.52× 1.00×
yarp-reverse-http2 dotnet-httpclient 29,082
(122 MiB / 50.3% CPU)
1.00× 1.94×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 34,243
(118 MiB / 51.6% CPU)
1.18× 2.28×

macOS (macos-15, Apple Silicon)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 🥇 32,302
(82 MiB / 22.7% CPU)
1.01× 1.00×
yarp-reverse-http2 dotnet-httpclient 31,922
(154 MiB / 39.3% CPU)
1.00× 0.99×
twp-reverse-http2-cleartext dotnet-httpclient 29,912
(127 MiB / 38.7% CPU)
0.94× 0.93×

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.

Medals are nginx / YARP / Titanium only, from the same run as Blocks A and B (37156912437). On the Linux run HAProxy was 23,420 (87 MiB / 26.7% CPU) and Envoy 3,838 (136 MiB / 24.5% CPU). On the macOS run HAProxy was 33,360 (101 MiB / 27.7% CPU) and Envoy 6,764 (129 MiB / 38.3% CPU).

Windows (windows-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient Not possible (no QUIC) — —
yarp-reverse-http3-cleartext dotnet-httpclient 23,173
(171 MiB / 51.9% CPU)
1.00× —
twp-reverse-http3-cleartext dotnet-httpclient 🥇 26,239
(115 MiB / 43.6% CPU)
1.13× —

Linux (ubuntu-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 0
(peak 15,029 · 107 MiB / 22.4% CPU)
0.80× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 18,681
(182 MiB / 50.7% CPU)
1.00× 1.24×
twp-reverse-http3-cleartext dotnet-httpclient 🥇 20,402
(142 MiB / 50.2% CPU)
1.09× 1.36×

macOS (macos-15, Apple Silicon)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 0
(peak 26,913 · 88 MiB / 19.0% CPU)
1.24× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 🥇 21,658
(229 MiB / 44.6% CPU)
1.00× 0.80×
twp-reverse-http3-cleartext dotnet-httpclient 18,385
(140 MiB / 44.6% CPU)
0.85× 0.68×

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 @ 0386b2aa — compare-product 37138944205. 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,718
(73 MiB / 48.6% CPU)
13,568
(125 MiB / 24.7% CPU)
21,289
(88 MiB / 50.3% CPU)
HTTP/1 · plain HTTP/1 · TLS 🥇 63,316
(88 MiB / 48.7% CPU)
27,918
(136 MiB / 24.8% CPU)
60,829
(97 MiB / 49.8% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 37,192
(109 MiB / 46.6% CPU)
Not possible (no H2 upstream) 32,784
(93 MiB / 48.2% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 38,763
(117 MiB / 45.1% CPU)
Not possible (no H2 upstream) 36,723
(95 MiB / 48.6% CPU)
HTTP/1 · plain HTTP/3 · QUIC 🥇 30,111
(117 MiB / 50.3% CPU)
Not possible (no H3 upstream) 29,414
(128 MiB / 51.4% CPU)
HTTP/1 · TLS HTTP/1 · plain 🥇 21,006
(84 MiB / 48.8% CPU)
8,717
(142 MiB / 24.7% CPU)
17,968
(102 MiB / 48.2% CPU)
HTTP/1 · TLS HTTP/1 · TLS 🥇 28,401
(85 MiB / 48.3% CPU)
12,590
(144 MiB / 24.8% CPU)
24,927
(102 MiB / 48.2% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 28,111
(111 MiB / 45.8% CPU)
Not possible (no H2 upstream) 26,458
(103 MiB / 47% CPU)
HTTP/1 · TLS HTTP/2 · TLS 🥇 24,680
(114 MiB / 42.6% CPU)
Not possible (no H2 upstream) 23,968
(112 MiB / 45.3% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 16,100
(105 MiB / 49% CPU)
Not possible (no H3 upstream) 15,440
(128 MiB / 50.2% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 36,931
(88 MiB / 52.9% CPU)
9,197
(127 MiB / 24.8% CPU)
32,084
(89 MiB / 54.6% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 31,632
(101 MiB / 53% CPU)
6,653
(139 MiB / 24.8% CPU)
27,576
(97 MiB / 51.8% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 111,921
(59 MiB / 29.2% CPU)
Not possible (no H2 upstream) 66,064
(93 MiB / 51.5% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 89,109
(63 MiB / 29.6% CPU)
Not possible (no H2 upstream) 56,161
(100 MiB / 48.5% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 45,242
(139 MiB / 54.2% CPU)
Not possible (no H3 upstream) 41,353
(139 MiB / 52.4% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 35,150
(97 MiB / 51.8% CPU)
8,094
(141 MiB / 24.5% CPU)
28,547
(95 MiB / 52.3% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 45,544
(108 MiB / 49.7% CPU)
11,455
(144 MiB / 24.4% CPU)
39,826
(98 MiB / 49.6% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 106,542
(77 MiB / 29.9% CPU)
Not possible (no H2 upstream) 59,502
(102 MiB / 52.6% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 111,358
(78 MiB / 30.6% CPU)
Not possible (no H2 upstream) 65,776
(97 MiB / 49% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 43,671
(141 MiB / 52.9% CPU)
Not possible (no H3 upstream) 37,298
(136 MiB / 53% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 15,288
(103 MiB / 45.6% CPU)
Not possible (no QUIC) 14,486
(158 MiB / 51.2% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 13,559
(112 MiB / 45.6% CPU)
Not possible (no QUIC) 13,225
(145 MiB / 51.2% CPU)
HTTP/3 · QUIC HTTP/2 · plain 🥇 35,201
(129 MiB / 45.2% CPU)
Not possible (no H2 upstream) 23,716
(172 MiB / 48.4% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 31,884
(131 MiB / 45.7% CPU)
Not possible (no H2 upstream) 21,920
(151 MiB / 47.7% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 16,882
(117 MiB / 47.2% CPU)
Not possible (no H3 upstream) 12,348
(150 MiB / 49.9% CPU)

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 @ 0386b2aa — compare-product 37138944205. 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 32,632
(84 MiB / 50.1% CPU)
🥇 39,708
(76 MiB / 41.3% CPU)
36,675
(66 MiB / 43.6% CPU)
20,251
(115 MiB / 62.5% CPU)
28,589
(115 MiB / 50.3% CPU)
HTTP/1 · plain HTTP/1 · TLS 24,764
(102 MiB / 49.8% CPU)
🥇 28,840
(92 MiB / 42.5% CPU)
27,741
(68 MiB / 44.5% CPU)
17,414
(117 MiB / 58.4% CPU)
21,704
(132 MiB / 50.6% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 80,883
(133 MiB / 49.8% CPU)
Not possible (no H2 upstream) 49,902
(66 MiB / 41.3% CPU)
57,690
(116 MiB / 53.8% CPU)
71,970
(129 MiB / 47.5% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 70,384
(155 MiB / 48.8% CPU)
Not possible (no H2 upstream) 65,133
(66 MiB / 42.6% CPU)
55,862
(117 MiB / 53.6% CPU)
64,060
(129 MiB / 45.9% CPU)
HTTP/1 · plain HTTP/3 · QUIC 🥇 53,655
(159 MiB / 54.8% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 29,207
(121 MiB / 47.6% CPU)
45,267
(170 MiB / 47.2% CPU)
HTTP/1 · TLS HTTP/1 · plain 37,443
(108 MiB / 49.1% CPU)
🥇 43,780
(102 MiB / 41.4% CPU)
43,464
(83 MiB / 43.8% CPU)
27,932
(127 MiB / 57.3% CPU)
32,652
(133 MiB / 50.2% CPU)
HTTP/1 · TLS HTTP/1 · TLS 20,479
(111 MiB / 48.3% CPU)
22,614
(103 MiB / 41.6% CPU)
🥇 22,867
(86 MiB / 42.8% CPU)
15,496
(127 MiB / 56.4% CPU)
17,542
(142 MiB / 50.1% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 53,639
(157 MiB / 48.2% CPU)
Not possible (no H2 upstream) 45,206
(85 MiB / 39.2% CPU)
40,719
(125 MiB / 56.7% CPU)
48,562
(146 MiB / 49% CPU)
HTTP/1 · TLS HTTP/2 · TLS 47,125
(162 MiB / 47% CPU)
Not possible (no H2 upstream) 🥇 52,892
(85 MiB / 40.4% CPU)
38,120
(127 MiB / 55.7% CPU)
43,703
(146 MiB / 48% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 18,332
(142 MiB / 52.3% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 9,524
(130 MiB / 55.4% CPU)
16,417
(158 MiB / 49.8% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 38,451
(114 MiB / 52.8% CPU)
16,655
(79 MiB / 19% CPU)
23,689
(67 MiB / 24.6% CPU)
14,151
(118 MiB / 23.3% CPU)
34,302
(114 MiB / 50.5% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 53,871
(132 MiB / 45.3% CPU)
36,494
(102 MiB / 19.6% CPU)
45,460
(70 MiB / 24.6% CPU)
28,035
(119 MiB / 23.2% CPU)
51,217
(124 MiB / 46.4% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 93,213
(87 MiB / 39% CPU)
Not possible (no H2 upstream) 29,683
(66 MiB / 24.3% CPU)
25,025
(116 MiB / 21.7% CPU)
54,957
(122 MiB / 46.7% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 58,499
(92 MiB / 36.4% CPU)
Not possible (no H2 upstream) 19,745
(69 MiB / 24.4% CPU)
16,269
(117 MiB / 21.4% CPU)
39,312
(131 MiB / 46.2% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 28,699
(139 MiB / 50.6% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 4,384
(121 MiB / 24.7% CPU)
26,459
(152 MiB / 47.4% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 34,790
(117 MiB / 51.8% CPU)
15,027
(100 MiB / 19.2% CPU)
20,595
(82 MiB / 24.6% CPU)
14,224
(128 MiB / 22.9% CPU)
29,235
(121 MiB / 50.3% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 27,313
(127 MiB / 49.9% CPU)
12,471
(110 MiB / 19.9% CPU)
17,008
(83 MiB / 24.3% CPU)
12,882
(129 MiB / 23.2% CPU)
23,308
(126 MiB / 50.2% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 83,894
(104 MiB / 33.6% CPU)
Not possible (no H2 upstream) 34,166
(83 MiB / 24.1% CPU)
19,477
(128 MiB / 21.4% CPU)
45,281
(131 MiB / 45.6% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 105,834
(101 MiB / 33.8% CPU)
Not possible (no H2 upstream) 49,401
(83 MiB / 24.2% CPU)
28,708
(127 MiB / 21% CPU)
60,670
(136 MiB / 43.9% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 26,506
(152 MiB / 49.3% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 4,391
(129 MiB / 24.8% CPU)
23,067
(162 MiB / 47.3% CPU)
HTTP/3 · QUIC HTTP/1 · plain 21,432
(142 MiB / 50.4% CPU)
0
(peak 15,402 · 105 MiB / 22.5% CPU)
🥇 23,096
(87 MiB / 27.1% CPU)
3,855
(134 MiB / 24.6% CPU)
18,435
(182 MiB / 50.4% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 24,920
(159 MiB / 48.3% CPU)
0
(peak 19,115 · 120 MiB / 22.4% CPU)
🥇 27,176
(91 MiB / 28.7% CPU)
1,058
(138 MiB / 4.6% CPU)
22,554
(201 MiB / 49% CPU)
HTTP/3 · QUIC HTTP/2 · plain 🥇 29,202
(157 MiB / 52.8% CPU)
Not possible (no H2 upstream) 25,453
(86 MiB / 26% CPU)
422
(130 MiB / 2.3% CPU)
23,989
(197 MiB / 48.4% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 25,256
(155 MiB / 50% CPU)
Not possible (no H2 upstream) 21,256
(85 MiB / 26.6% CPU)
514
(133 MiB / 2.8% CPU)
21,430
(196 MiB / 47.6% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 42,187
(208 MiB / 49.3% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 157
(129 MiB / 0.5% CPU)
31,146
(233 MiB / 46.3% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 37138944205). 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.25× and Full ≥ 0.25× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP ÷ closest peer ≥ 0.50×.

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 33,259
(92 MiB / 50.4% CPU)
32,587
(89 MiB / 49.7% CPU)
1.02× 1×
HTTP/1 · plain HTTP/1 · TLS 24,374
(110 MiB / 50.7% CPU)
24,526
(110 MiB / 50.5% CPU)
0.98× 0.99×
HTTP/1 · plain HTTP/2 · plain 78,913
(135 MiB / 51% CPU)
77,344
(137 MiB / 51.6% CPU)
0.98× 0.96×
HTTP/1 · plain HTTP/2 · TLS 69,934
(149 MiB / 49% CPU)
67,540
(157 MiB / 49.6% CPU)
0.99× 0.96×
HTTP/1 · plain HTTP/3 · QUIC 49,997
(174 MiB / 56.3% CPU)
51,671
(171 MiB / 55.8% CPU)
0.93× 0.96×
HTTP/1 · TLS HTTP/1 · plain 37,625
(115 MiB / 49.8% CPU)
36,499
(116 MiB / 49.1% CPU)
1× 0.97×
HTTP/1 · TLS HTTP/1 · TLS 20,555
(115 MiB / 49.2% CPU)
19,875
(115 MiB / 49% CPU)
1× 0.97×
HTTP/1 · TLS HTTP/2 · plain 53,451
(145 MiB / 50.7% CPU)
51,888
(147 MiB / 50.6% CPU)
1× 0.97×
HTTP/1 · TLS HTTP/2 · TLS 46,958
(153 MiB / 48.9% CPU)
46,309
(154 MiB / 49.6% CPU)
1× 0.98×
HTTP/1 · TLS HTTP/3 · QUIC 17,442
(153 MiB / 52.6% CPU)
16,947
(152 MiB / 52.9% CPU)
0.95× 0.92×
HTTP/2 · plain HTTP/1 · plain 37,304
(121 MiB / 53.8% CPU)
35,995
(124 MiB / 53.7% CPU)
0.97× 0.94×
HTTP/2 · plain HTTP/1 · TLS 52,626
(136 MiB / 46.2% CPU)
51,109
(134 MiB / 46.6% CPU)
0.98× 0.95×
HTTP/2 · plain HTTP/2 · plain 70,046
(95 MiB / 43.8% CPU)
64,950
(94 MiB / 42.7% CPU)
0.75× 0.7×
HTTP/2 · plain HTTP/2 · TLS 48,692
(97 MiB / 40.1% CPU)
46,169
(96 MiB / 39.9% CPU)
0.83× 0.79×
HTTP/2 · plain HTTP/3 · QUIC 28,077
(144 MiB / 50.8% CPU)
27,749
(145 MiB / 51.3% CPU)
0.98× 0.97×
HTTP/2 · TLS HTTP/1 · plain 33,799
(118 MiB / 53.1% CPU)
33,370
(119 MiB / 53.1% CPU)
0.97× 0.96×
HTTP/2 · TLS HTTP/1 · TLS 26,984
(122 MiB / 51.6% CPU)
25,959
(128 MiB / 51.2% CPU)
0.99× 0.95×
HTTP/2 · TLS HTTP/2 · plain 63,878
(109 MiB / 37.2% CPU)
60,327
(113 MiB / 36.8% CPU)
0.76× 0.72×
HTTP/2 · TLS HTTP/2 · TLS 83,024
(111 MiB / 35.7% CPU)
80,237
(112 MiB / 35.3% CPU)
0.78× 0.76×
HTTP/2 · TLS HTTP/3 · QUIC 26,796
(155 MiB / 50.8% CPU)
26,187
(152 MiB / 50.9% CPU)
1.01× 0.99×
HTTP/3 · QUIC HTTP/1 · plain 20,453
(140 MiB / 51.4% CPU)
19,695
(143 MiB / 50.5% CPU)
0.95× 0.92×
HTTP/3 · QUIC HTTP/1 · TLS 24,865
(157 MiB / 49.6% CPU)
23,742
(158 MiB / 49% CPU)
1× 0.95×
HTTP/3 · QUIC HTTP/2 · plain 29,174
(157 MiB / 53.8% CPU)
28,145
(159 MiB / 53.5% CPU)
1× 0.96×
HTTP/3 · QUIC HTTP/2 · TLS 24,698
(153 MiB / 51.1% CPU)
24,065
(152 MiB / 50.3% CPU)
0.98× 0.95×
HTTP/3 · QUIC HTTP/3 · QUIC 37,404
(201 MiB / 52.5% CPU)
38,997
(206 MiB / 53.1% CPU)
0.89× 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 @ 0386b2aa — compare-product 37138944205. 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 11,350
(115 MiB / 19.3% CPU)
16,390
(68 MiB / 15.7% CPU)
🥇 16,634
(83 MiB / 17.7% CPU)
6,996
(112 MiB / 21.6% CPU)
5,588
(159 MiB / 12.1% CPU)
HTTP/1 · plain HTTP/1 · TLS 19,968
(129 MiB / 24.9% CPU)
🥇 25,998
(78 MiB / 21.8% CPU)
19,833
(88 MiB / 20.1% CPU)
11,274
(114 MiB / 30.5% CPU)
22,188
(164 MiB / 28.3% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 21,519
(129 MiB / 20.2% CPU)
Not possible (no H2 upstream) 19,366
(84 MiB / 20.1% CPU)
11,218
(111 MiB / 25% CPU)
11,842
(166 MiB / 13.4% CPU)
HTTP/1 · plain HTTP/2 · TLS 17,272
(169 MiB / 20.6% CPU)
Not possible (no H2 upstream) 🥇 19,290
(85 MiB / 22.1% CPU)
18,595
(112 MiB / 44.8% CPU)
12,441
(177 MiB / 14.4% CPU)
HTTP/1 · plain HTTP/3 · QUIC 7,979
(129 MiB / 15% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 9,001
(116 MiB / 27.1% CPU)
🥇 14,647
(163 MiB / 22% CPU)
HTTP/1 · TLS HTTP/1 · plain 8,027
(125 MiB / 19.2% CPU)
🥇 17,442
(84 MiB / 17.5% CPU)
10,406
(100 MiB / 19.7% CPU)
15,576
(122 MiB / 37.2% CPU)
9,049
(162 MiB / 21.5% CPU)
HTTP/1 · TLS HTTP/1 · TLS 12,065
(154 MiB / 25.1% CPU)
🥇 16,714
(89 MiB / 17.9% CPU)
9,055
(103 MiB / 17.9% CPU)
6,018
(122 MiB / 20.8% CPU)
15,945
(180 MiB / 12.1% CPU)
HTTP/1 · TLS HTTP/2 · plain 33,147
(155 MiB / 40.3% CPU)
Not possible (no H2 upstream) 18,269
(101 MiB / 30.7% CPU)
27,665
(121 MiB / 45.4% CPU)
🥇 45,625
(165 MiB / 36.5% CPU)
HTTP/1 · TLS HTTP/2 · TLS 11,455
(207 MiB / 17.2% CPU)
Not possible (no H2 upstream) 🥇 30,959
(101 MiB / 27.9% CPU)
23,149
(120 MiB / 38.3% CPU)
4,878
(223 MiB / 7.6% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 6,409
(152 MiB / 23.6% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 7,288
(125 MiB / 35.9% CPU)
🥇 7,959
(183 MiB / 22.8% CPU)
HTTP/2 · plain HTTP/1 · plain 19,856
(133 MiB / 20.8% CPU)
🥇 21,532
(70 MiB / 17% CPU)
7,455
(87 MiB / 7.2% CPU)
9,828
(112 MiB / 18.2% CPU)
11,796
(151 MiB / 14.3% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 39,072
(134 MiB / 35% CPU)
22,480
(80 MiB / 22.6% CPU)
37,659
(92 MiB / 20.2% CPU)
15,170
(114 MiB / 17.5% CPU)
36,270
(162 MiB / 34.7% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 38,837
(115 MiB / 9.8% CPU)
Not possible (no H2 upstream) 35,788
(87 MiB / 18.4% CPU)
20,918
(109 MiB / 17.9% CPU)
34,366
(164 MiB / 19.1% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 20,738
(118 MiB / 6.4% CPU)
Not possible (no H2 upstream) 19,404
(89 MiB / 18.2% CPU)
18,552
(111 MiB / 26.2% CPU)
6,645
(167 MiB / 7.3% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 10,950
(139 MiB / 20.9% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 10,452
(114 MiB / 23.7% CPU)
4,648
(167 MiB / 9.7% CPU)
HTTP/2 · TLS HTTP/1 · plain 18,702
(132 MiB / 19.7% CPU)
19,621
(81 MiB / 21.2% CPU)
🥇 36,352
(101 MiB / 19.4% CPU)
17,403
(121 MiB / 21.3% CPU)
9,754
(154 MiB / 17.5% CPU)
HTTP/2 · TLS HTTP/1 · TLS 9,044
(140 MiB / 20.3% CPU)
🥇 17,051
(86 MiB / 15.6% CPU)
12,741
(104 MiB / 18% CPU)
8,500
(121 MiB / 13.9% CPU)
4,103
(168 MiB / 9.4% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 68,492
(130 MiB / 22.1% CPU)
Not possible (no H2 upstream) 27,664
(101 MiB / 19.1% CPU)
19,693
(119 MiB / 20.7% CPU)
28,016
(158 MiB / 16.2% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 88,279
(136 MiB / 33% CPU)
Not possible (no H2 upstream) 37,474
(102 MiB / 28.7% CPU)
22,960
(119 MiB / 25.1% CPU)
47,514
(159 MiB / 42.4% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 5,481
(152 MiB / 11% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 🥇 8,025
(123 MiB / 20% CPU)
4,994
(171 MiB / 8.9% CPU)
HTTP/3 · QUIC HTTP/1 · plain 8,392
(131 MiB / 20.4% CPU)
0
(peak 14,230 · 87 MiB / 13.3% CPU)
🥇 15,556
(101 MiB / 20.2% CPU)
5,735
(130 MiB / 32.9% CPU)
5,035
(244 MiB / 16.8% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 12,889
(152 MiB / 28.8% CPU)
0
(peak 14,318 · 92 MiB / 18.5% CPU)
🥇 17,962
(104 MiB / 22.1% CPU)
4,688
(130 MiB / 27.4% CPU)
12,974
(239 MiB / 33.9% CPU)
HTTP/3 · QUIC HTTP/2 · plain 15,867
(139 MiB / 21.5% CPU)
Not possible (no H2 upstream) 🥇 21,919
(102 MiB / 19.7% CPU)
9,652
(125 MiB / 32.5% CPU)
10,054
(267 MiB / 14.1% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 28,390
(146 MiB / 24.8% CPU)
Not possible (no H2 upstream) 🥇 34,002
(102 MiB / 20.4% CPU)
11,891
(125 MiB / 28.3% CPU)
26,452
(232 MiB / 28% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 4,442
(138 MiB / 9% CPU)
Not possible (no H3 upstream) Not possible (no H3 upstream) 7,144
(124 MiB / 38.2% CPU)
🥇 23,628
(226 MiB / 44.2% CPU)

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/cache) runs on H1 terminate-lite without SessionEventArgs; a cache hit skips the origin. JWT caches successful bearer validations. CORS only adds response headers on the way out, so it stays on terminate-lite. Circuit breaker and idempotent retry have to see the request or the status code, so they stay on the session path and should land near the intercept row. Maintainer gate thresholds live under Maintainer notes.

Median of 3 repeats. Linux rows in this paste were re-measured @ 8cf8b770 (Actions 37192183576). Other edition rows stay @ 0386b2aa. 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%). The Gate column is that script's floor.

Arm Linux Lin÷ Gate
twp-cli-reverse-http1 vs library 48,005
(172 MiB / 48.5% CPU)
1.04× ≥ 0.50×
twp-cli-reverse-http1-tls vs library TLS 35,819
(203 MiB / 48.7% CPU)
1.02× ≥ 0.50×
twp-cli-reverse-http1-route vs CLI 47,759
(174 MiB / 48.4% CPU)
0.99× ≥ 0.50×
twp-cli-plus-base-http1 vs CLI 47,791
(174 MiB / 48.5% CPU)
1.00× ≥ 0.50×
twp-cli-plus-cache-http1 (cold) vs CLI 122,554
(169 MiB / 48.0% CPU)
1.80× ≥ 0.50×
twp-cli-intercept-http1 vs CLI 35,573
(183 MiB / 52.1% CPU)
0.74× ≥ 0.50×
twp-cli-plus-waf-http1 vs CLI 46,484
(179 MiB / 48.9% CPU)
0.97× ≥ 0.50×
twp-cli-plus-cidr-http1 vs CLI 46,646
(178 MiB / 48.9% CPU)
0.97× ≥ 0.50×
twp-cli-plus-jwt-http1 vs CLI 43,613
(199 MiB / 48.7% CPU)
0.91× ≥ 0.50×
twp-cli-plus-ratelimit-http1 vs CLI 46,620
(177 MiB / 49.1% CPU)
0.97× ≥ 0.50×
twp-cli-plus-resilience-http1 vs CLI 47,710
(181 MiB / 48.4% CPU)
0.99× ≥ 0.50×
twp-cli-plus-discovery-file-http1 vs CLI 47,630
(180 MiB / 48.5% CPU)
0.99× ≥ 0.50×
twp-cli-plus-metrics-scrape-http1 vs CLI 48,257
(183 MiB / 48.5% CPU)
1.01× ≥ 0.50×
twp-cli-plus-cache-hit-http1 vs cache cold 120,058
(169 MiB / 47.3% CPU)
0.98× ≥ 0.50×
twp-cli-plus-cors-http1 vs CLI 31,203
(169 MiB / 51.0% CPU)
0.92× ≥ 0.50×
twp-cli-plus-circuit-http1 vs CLI 45,001
(243 MiB / 50.3% CPU)
0.86× ≥ 0.50×
twp-cli-plus-retry-http1 vs CLI 30,430
(177 MiB / 50.2% CPU)
0.81× ≥ 0.50×
twp-cli-static-http1 vs CLI 80,449
(175 MiB / 50.3% CPU)
1.68× ≥ 0.50×
twp-cli-logging-http1 vs CLI 47,905
(175 MiB / 48.5% CPU)
1.00× ≥ 0.50×
twp-cli-lb-leasttime-http1 vs route 43,887
(197 MiB / 50.7% CPU)
0.92× ≥ 0.50×
twp-cli-dialect-twp-http1 vs CLI 47,368
(168 MiB / 48.3% CPU)
0.99× ≥ 0.50×

validate-edition-gates.ps1 floors are 0.50×. Each ÷ column uses the two arms from the same job. Circuit breaker and idempotent retry stay on the session path, so a ratio near intercept is expected. Laptop smoke ratios stay on Performance Local Lab — Editions.

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 (@ 0386b2aa), 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 @ 0386b2aa. Source: Actions 37139047700 (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,209
(89 MiB / 44.8% CPU)
571
(141 MiB / 24.4% CPU)
7,978
(137 MiB / 51.3% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 7,957
(170 MiB / 46.5% CPU)
555
(142 MiB / 24.8% CPU)
6,580
(134 MiB / 50.6% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 4,140
(129 MiB / 37.4% CPU)
Not possible (no QUIC) 3,984
(179 MiB / 48.8% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 2,846
(126 MiB / 47.1% CPU)
173
(142 MiB / 24.8% CPU)
2,689
(135 MiB / 47.9% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 4,398
(155 MiB / 34.0% CPU)
306
(142 MiB / 24.8% CPU)
3,753
(135 MiB / 41.6% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 1,140
(113 MiB / 38.8% CPU)
Not possible (no QUIC) 1,096
(177 MiB / 44.3% CPU)
64 KiB HTTP/2 · plain HTTP/1 · plain 20,936
(163 MiB / 35.6% CPU)
4,959
(127 MiB / 24.6% CPU)
🥇 23,518
(106 MiB / 44.2% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 10,843
(118 MiB / 39.3% CPU)
Not possible 7,247
(142 MiB / 51.0% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 7,380
(122 MiB / 39.0% CPU)
Not possible 5,547
(142 MiB / 48.2% CPU)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 3,985
(124 MiB / 40.0% CPU)
Not possible 3,755
(198 MiB / 48.2% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 4,000
(143 MiB / 40.5% CPU)
Not possible (no QUIC) 3,361
(193 MiB / 49.5% CPU)
256 KiB HTTP/2 · plain HTTP/1 · plain 🥇 3,240
(149 MiB / 31.6% CPU)
842
(127 MiB / 24.4% CPU)
2,740
(124 MiB / 37.5% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 2,591
(152 MiB / 37.1% CPU)
Not possible 1,874
(167 MiB / 46.7% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 1,868
(129 MiB / 35.2% CPU)
Not possible 1,495
(148 MiB / 43.8% CPU)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 870
(148 MiB / 42.1% CPU)
Not possible 🥇 990
(198 MiB / 44.5% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,030
(145 MiB / 39.2% CPU)
Not possible (no QUIC) 975
(195 MiB / 44.0% CPU)

nginx/Windows collapses on large reverse bodies in this harness; treat as same-OS only. Prefer the ratio columns; absolute RPS swings by VM.

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

Median of 3 repeats @ 0386b2aa. Source: Actions 37139047700 (compare-bodies). Warmup 2s / measure 8s.

Body Client Origin TWP nginx HAProxy Envoy YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 11,499
(122 MiB / 40.7% CPU)
8,186
(100 MiB / 45.2% CPU)
🥇 12,944
(85 MiB / 36.9% CPU)
9,632
(132 MiB / 46.7% CPU)
8,511
(166 MiB / 48.1% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 8,703
(241 MiB / 39.1% CPU)
3,316
(103 MiB / 13.0% CPU)
🥇 9,029
(84 MiB / 23.7% CPU)
7,535
(142 MiB / 23.1% CPU)
7,121
(165 MiB / 46.5% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 11,644
(204 MiB / 42.2% CPU)
2,882
(104 MiB / 22.4% CPU)
9,769
(92 MiB / 32.1% CPU)
1
(136 MiB / 0.1% CPU)
8,428
(244 MiB / 51.0% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 3,869
(131 MiB / 31.6% CPU)
2,488
(102 MiB / 49.3% CPU)
🥇 4,108
(83 MiB / 28.1% CPU)
3,659
(145 MiB / 32.0% CPU)
3,083
(171 MiB / 41.9% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 3,156
(219 MiB / 31.0% CPU)
1,239
(103 MiB / 19.9% CPU)
🥇 3,594
(84 MiB / 19.1% CPU)
3,430
(166 MiB / 20.3% CPU)
3,030
(162 MiB / 37.9% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 2,130
(163 MiB / 42.8% CPU)
637
(113 MiB / 23.2% CPU)
1,893
(88 MiB / 29.3% CPU)
1,144
(143 MiB / 19.5% CPU)
1,770
(227 MiB / 48.2% CPU)
64 KiB HTTP/2 · plain HTTP/1 · plain 🥇 10,441
(231 MiB / 42.2% CPU)
2,853
(80 MiB / 18.1% CPU)
7,618
(70 MiB / 24.4% CPU)
6,838
(133 MiB / 24.1% CPU)
8,649
(153 MiB / 45.9% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 5,820
(157 MiB / 36.5% CPU)
Not possible 2,095
(87 MiB / 24.6% CPU)
4,340
(144 MiB / 22.2% CPU)
4,490
(184 MiB / 45.6% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 6,264
(159 MiB / 36.9% CPU)
Not possible 2,627
(82 MiB / 24.4% CPU)
4,673
(147 MiB / 23.0% CPU)
5,327
(180 MiB / 44.0% CPU)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 3,724
(169 MiB / 44.7% CPU)
Not possible 2,088
(90 MiB / 35.4% CPU)
25
(143 MiB / 0.4% CPU)
3,029
(231 MiB / 48.8% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 4,041
(193 MiB / 44.7% CPU)
1,460
(peak 1,481 · 111 MiB / 22.3% CPU)
3,083
(96 MiB / 31.6% CPU)
6
(138 MiB / 0.2% CPU)
3,194
(253 MiB / 49.5% CPU)
256 KiB HTTP/2 · plain HTTP/1 · plain 2,897
(201 MiB / 34.9% CPU)
901
(79 MiB / 18.9% CPU)
2,775
(69 MiB / 22.4% CPU)
2,922
(158 MiB / 21.8% CPU)
🥇 2,931
(154 MiB / 38.8% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 1,734
(168 MiB / 32.4% CPU)
Not possible 609
(95 MiB / 24.6% CPU)
1,694
(171 MiB / 22.7% CPU)
1,261
(187 MiB / 42.5% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 2,452
(177 MiB / 33.1% CPU)
Not possible 1,139
(89 MiB / 23.9% CPU)
1,840
(164 MiB / 22.7% CPU)
2,181
(194 MiB / 38.9% CPU)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 812
(187 MiB / 50.7% CPU)
Not possible 644
(93 MiB / 37.9% CPU)
634
(162 MiB / 22.3% CPU)
🥇 906
(227 MiB / 46.5% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 2,006
(188 MiB / 41.3% CPU)
746
(129 MiB / 18.4% CPU)
1,786
(96 MiB / 28.6% CPU)
1,312
(142 MiB / 20.4% CPU)
1,693
(241 MiB / 46.8% CPU)

Prefer the ratio columns; absolute RPS swings by VM.

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

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

Body Client Origin TWP nginx HAProxy Envoy YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 4,260
(191 MiB / 22.4% CPU)
🥇 6,206
(85 MiB / 20.4% CPU)
5,074
(102 MiB / 22.4% CPU)
5,600
(131 MiB / 32.1% CPU)
3,732
(172 MiB / 25.8% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 7,770
(429 MiB / 35.6% CPU)
7,476
(85 MiB / 24.4% CPU)
7,184
(102 MiB / 27.2% CPU)
🥇 8,761
(145 MiB / 27.6% CPU)
5,857
(176 MiB / 41.9% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 2,449
(273 MiB / 32.2% CPU)
0
(peak 2,351 · 113 MiB / 27.4% CPU)
1,407
(103 MiB / 30.8% CPU)
1,359
(140 MiB / 31.3% CPU)
1,428
(428 MiB / 32.5% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 1,699
(222 MiB / 21.9% CPU)
1,737
(85 MiB / 19.0% CPU)
🥇 2,400
(101 MiB / 29.3% CPU)
1,971
(154 MiB / 25.9% CPU)
1,610
(186 MiB / 32.6% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 991
(417 MiB / 21.5% CPU)
🥇 1,960
(85 MiB / 21.6% CPU)
1,654
(102 MiB / 20.0% CPU)
1,784
(156 MiB / 23.3% CPU)
1,066
(179 MiB / 27.6% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 500
(181 MiB / 32.8% CPU)
331
(81 MiB / 14.3% CPU)
🥇 578
(101 MiB / 33.3% CPU)
358
(160 MiB / 31.1% CPU)
285
(450 MiB / 25.5% CPU)
64 KiB HTTP/2 · plain HTTP/1 · plain 5,880
(419 MiB / 16.0% CPU)
🥇 9,280
(73 MiB / 22.4% CPU)
8,522
(89 MiB / 23.8% CPU)
6,415
(135 MiB / 26.8% CPU)
8,242
(176 MiB / 38.6% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · plain 4,883
(322 MiB / 17.7% CPU)
Not possible 3,716
(103 MiB / 19.9% CPU)
5,955
(130 MiB / 27.4% CPU)
🥇 8,119
(246 MiB / 45.1% CPU)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 3,580
(269 MiB / 20.5% CPU)
Not possible 2,177
(102 MiB / 19.3% CPU)
3,459
(130 MiB / 23.8% CPU)
2,472
(234 MiB / 22.0% CPU)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 1,701
(203 MiB / 30.6% CPU)
Not possible 1,615
(103 MiB / 32.6% CPU)
1,207
(134 MiB / 41.4% CPU)
🥇 1,785
(467 MiB / 33.9% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,768
(239 MiB / 29.3% CPU)
0
(peak 1,942 · 113 MiB / 19.9% CPU)
1,702
(108 MiB / 32.3% CPU)
996
(140 MiB / 35.1% CPU)
987
(389 MiB / 23.9% CPU)
256 KiB HTTP/2 · plain HTTP/1 · plain 1,156
(385 MiB / 14.6% CPU)
2,954
(72 MiB / 17.4% CPU)
🥇 2,996
(88 MiB / 19.3% CPU)
2,673
(146 MiB / 19.7% CPU)
1,660
(206 MiB / 21.7% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · plain 1,553
(625 MiB / 23.6% CPU)
Not possible 686
(114 MiB / 18.1% CPU)
🥇 1,946
(156 MiB / 22.5% CPU)
1,105
(308 MiB / 23.5% CPU)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 961
(541 MiB / 23.5% CPU)
Not possible 619
(101 MiB / 18.1% CPU)
🥇 1,271
(151 MiB / 24.2% CPU)
1,030
(314 MiB / 28.6% CPU)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 310
(peak 322 · 323 MiB / 26.4% CPU)
Not possible 🥇 379
(103 MiB / 29.0% CPU)
243
(126 MiB / 35.3% CPU)
367
(503 MiB / 27.4% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 543
(240 MiB / 31.6% CPU)
400
(peak 466 · 83 MiB / 22.2% CPU)
438
(104 MiB / 31.1% CPU)
294
(130 MiB / 37.7% CPU)
382
(442 MiB / 32.1% CPU)

Windows — POST 64 KiB request + 64 KiB response

Median of 3 repeats on windows-latest @ 0386b2aa. Source: Actions 37139137891 (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 🥇 5,871
(101 MiB / 44.4% CPU)
362
(142 MiB / 24.6% CPU)
4,143
(136 MiB / 57.0% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 4,111
(180 MiB / 49.4% CPU)
361
(144 MiB / 24.8% CPU)
3,522
(142 MiB / 51.4% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 2,081
(178 MiB / 40.7% CPU)
Not possible (no QUIC) 2,081
(210 MiB / 50.8% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 6,696
(193 MiB / 45.7% CPU)
2,165
(131 MiB / 24.8% CPU)
6,333
(125 MiB / 51.9% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 5,203
(118 MiB / 36.4% CPU)
Not possible 3,438
(146 MiB / 43.9% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 5,556
(127 MiB / 37.9% CPU)
Not possible 4,138
(145 MiB / 48.2% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 2,162
(176 MiB / 42.6% CPU)
Not possible 1,890
(205 MiB / 48.2% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,817
(186 MiB / 41.2% CPU)
Not possible (no QUIC) 1,812
(218 MiB / 50.6% CPU)

TWP leads H2→H1 POST (about 1.1–1.3× YARP) and H3 POST (about 1.0–1.1× YARP). H2 TLS→H2 TLS POST sustain is about 1.2–1.4× YARP.

Linux — POST 64 KiB request + 64 KiB response

Median of 3 repeats @ 0386b2aa. Source: Actions 37139137891 (compare-post).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 4,911
(131 MiB / 43.0% CPU)
3,919
(100 MiB / 47.8% CPU)
🥇 5,183
(83 MiB / 39.9% CPU)
5,162
(132 MiB / 40.4% CPU)
3,337
(172 MiB / 54.4% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 4,484
(232 MiB / 41.6% CPU)
2,130
(118 MiB / 20.8% CPU)
2,955
(84 MiB / 24.3% CPU)
3,856
(peak 4,235 · 130 MiB / 20.9% CPU)
3,975
(166 MiB / 46.9% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 6,094
(247 MiB / 41.2% CPU)
975
(116 MiB / 24.8% CPU)
3,555
(90 MiB / 28.5% CPU)
0
(126 MiB)
5,337
(269 MiB / 47.0% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 7,209
(229 MiB / 42.9% CPU)
3,580
(97 MiB / 24.9% CPU)
3,908
(68 MiB / 22.9% CPU)
0
(peak 6,582 · 133 MiB / 22.4% CPU)
5,528
(159 MiB / 42.7% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 6,411
(180 MiB / 34.6% CPU)
Not possible 3,493
(88 MiB / 22.9% CPU)
5,385
(143 MiB / 21.2% CPU)
4,746
(178 MiB / 48.1% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 5,677
(180 MiB / 37.6% CPU)
Not possible 2,530
(88 MiB / 23.4% CPU)
4,285
(147 MiB / 22.2% CPU)
4,822
(175 MiB / 45.9% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 2,134
(226 MiB / 42.5% CPU)
Not possible 1,190
(96 MiB / 32.2% CPU)
0
(127 MiB / 0.1% CPU)
1,812
(252 MiB / 47.3% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 3,226
(249 MiB / 43.3% CPU)
612
(125 MiB / 24.9% CPU)
1,969
(97 MiB / 25.0% CPU)
0
(127 MiB / 0.1% CPU)
2,905
(266 MiB / 46.7% 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) @ 0386b2aa. Source: Actions 37139137891 (compare-post).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 3,019
(239 MiB / 19.0% CPU)
🥇 3,977
(89 MiB / 21.4% CPU)
3,964
(103 MiB / 26.8% CPU)
3,656
(131 MiB / 24.2% CPU)
2,272
(203 MiB / 33.7% CPU)
HTTP/2 · TLS HTTP/1 · plain 1,884
(429 MiB / 21.9% CPU)
🥇 3,252
(98 MiB / 20.7% CPU)
2,375
(101 MiB / 19.8% CPU)
0
(peak 3,445 · 146 MiB / 20.6% CPU)
1,606
(185 MiB / 22.6% CPU)
HTTP/3 · QUIC HTTP/1 · plain 1,021
(417 MiB / 28.5% CPU)
0
(peak 1,326 · 106 MiB / 23.7% CPU)
853
(101 MiB / 30.0% CPU)
472
(135 MiB / 36.0% CPU)
🥇 1,118
(501 MiB / 29.0% CPU)
HTTP/2 · plain HTTP/1 · plain 2,726
(378 MiB / 17.8% CPU)
🥇 4,777
(85 MiB / 17.5% CPU)
2,289
(87 MiB / 19.6% CPU)
0
(peak 2,511 · 114 MiB / 17.9% CPU)
2,642
(199 MiB / 24.0% CPU)
HTTP/2 · TLS HTTP/2 · plain 1,604
(287 MiB / 14.6% CPU)
Not possible 1,321
(103 MiB / 22.1% CPU)
🥇 2,760
(132 MiB / 21.9% CPU)
856
(222 MiB / 16.4% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 3,102
(277 MiB / 34.3% CPU)
Not possible 1,872
(102 MiB / 24.8% CPU)
2,205
(133 MiB / 25.9% CPU)
2,831
(204 MiB / 44.4% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 1,342
(283 MiB / 39.3% CPU)
Not possible 858
(105 MiB / 32.9% CPU)
514
(peak 648 · 128 MiB / 40.4% CPU)
1,031
(450 MiB / 40.8% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 828
(284 MiB / 27.5% CPU)
327
(83 MiB / 10.3% CPU)
715
(peak 752 · 107 MiB / 28.8% CPU)
334
(peak 450 · 130 MiB / 26.3% CPU)
736
(454 MiB / 25.5% 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 @ 0386b2aa — 37139186736 (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 🥇 675
(84 MiB / 2.1% CPU)
663
(142 MiB / 11.3% CPU)
671
(122 MiB / 2.1% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 62
(peak 91 · 113 MiB / 1.4% CPU)
18
(142 MiB / 0.4% CPU)
18
(86 MiB / 0.8% CPU)
HTTP/3 · QUIC HTTP/1 · plain 1,060
(126 MiB / 14.2% CPU)
Not possible (no QUIC) 🥇 1,333
(180 MiB / 28.5% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 60
(peak 85 · 111 MiB / 1.4% CPU)
18
(128 MiB / 0.1% CPU)
17
(78 MiB / 0.8% CPU)
HTTP/2 · TLS HTTP/2 · plain 16
(77 MiB / 0.6% CPU)
Not possible 🥇 18
(85 MiB / 0.8% CPU)
HTTP/2 · TLS HTTP/2 · TLS 16
(77 MiB / 0.7% CPU)
Not possible 🥇 18
(91 MiB / 1.3% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 1,065
(118 MiB / 9.2% CPU)
Not possible 🥇 1,610
(180 MiB / 28.2% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 799
(133 MiB / 16.7% CPU)
Not possible (no QUIC) 🥇 828
(177 MiB / 29.6% CPU)

H1, H2, and H3 loss sustain are in the table. Prefer the ratio columns.

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

Median of 3 repeats @ 0386b2aa. Source: 37139186736 (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
(121 MiB / 9.6% CPU)
🥇 1,220
(102 MiB / 8.1% CPU)
1,220
(83 MiB / 4.8% CPU)
1,211
(128 MiB / 6.5% CPU)
1,213
(151 MiB / 13.0% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 312
(184 MiB / 6.6% CPU)
40
(98 MiB / 0.3% CPU)
41
(85 MiB / 0.2% CPU)
40
(137 MiB / 0.3% CPU)
40
(127 MiB / 1.5% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 1,152
(166 MiB / 24.3% CPU)
1,094
(114 MiB / 23.8% CPU)
946
(87 MiB / 19.4% CPU)
1,043
(138 MiB / 24.5% CPU)
1,132
(240 MiB / 31.4% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 323
(174 MiB / 6.0% CPU)
40
(78 MiB / 0.2% CPU)
41
(69 MiB / 0.2% CPU)
40
(126 MiB / 0.3% CPU)
42
(121 MiB / 1.2% CPU)
HTTP/2 · TLS HTTP/2 · plain 40
(102 MiB / 0.8% CPU)
Not possible 🥇 41
(85 MiB / 0.3% CPU)
40
(133 MiB / 0.3% CPU)
40
(130 MiB / 1.3% CPU)
HTTP/2 · TLS HTTP/2 · TLS 40
(101 MiB / 1.0% CPU)
Not possible 40
(86 MiB / 0.5% CPU)
40
(137 MiB / 0.4% CPU)
🥇 40
(127 MiB / 1.4% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 1,385
(162 MiB / 22.1% CPU)
Not possible 1,177
(93 MiB / 21.4% CPU)
1,376
(141 MiB / 25.1% CPU)
🥇 1,408
(229 MiB / 30.9% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,194
(177 MiB / 25.6% CPU)
1,116
(117 MiB / 24.0% CPU)
970
(91 MiB / 21.3% CPU)
1,055
(139 MiB / 25.8% CPU)
1,112
(221 MiB / 33.0% 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) @ 0386b2aa. Source: 37139186736 (compare-lossy; lossy H3 uses quic-http3, UDP drop-only).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 391
(248 MiB / 4.0% CPU)
371
(83 MiB / 1.3% CPU)
421
(100 MiB / 1.9% CPU)
402
(134 MiB / 2.6% CPU)
🥇 463
(215 MiB / 4.9% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 19
(peak 29 · 132 MiB / 3.6% CPU)
8
(peak 9 · 81 MiB / 0.1% CPU)
8
(96 MiB / 0.2% CPU)
8
(118 MiB / 0.2% CPU)
8
(128 MiB / 1.0% CPU)
HTTP/3 · QUIC HTTP/1 · plain 1,178
(252 MiB / 27.1% CPU)
🥇 1,305
(98 MiB / 17.9% CPU)
843
(101 MiB / 22.4% CPU)
1,061
(132 MiB / 33.8% CPU)
1,023
(427 MiB / 31.0% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 18
(peak 30 · 123 MiB / 2.8% CPU)
8
(peak 9 · 69 MiB / 0.1% CPU)
8
(83 MiB / 0.1% CPU)
8
(109 MiB / 0.2% CPU)
9
(117 MiB / 7.3% CPU)
HTTP/2 · TLS HTTP/2 · plain 8
(128 MiB / 1.2% CPU)
Not possible 🥇 8
(97 MiB / 0.2% CPU)
8
(118 MiB / 0.2% CPU)
8
(129 MiB / 1.3% CPU)
HTTP/2 · TLS HTTP/2 · TLS 9
(118 MiB / 1.4% CPU)
Not possible 10
(98 MiB / 0.5% CPU)
10
(122 MiB / 0.4% CPU)
🥇 12
(144 MiB / 3.0% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 712
(185 MiB / 19.8% CPU)
Not possible 419
(105 MiB / 16.8% CPU)
🥇 800
(131 MiB / 28.6% CPU)
532
(439 MiB / 21.0% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 668
(215 MiB / 18.6% CPU)
916
(100 MiB / 14.1% CPU)
588
(106 MiB / 16.1% CPU)
🥇 1,041
(132 MiB / 27.8% CPU)
534
(383 MiB / 19.1% 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 @ 0386b2aa (37138997054). 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 🥇 256
(94 MiB / 2.7% CPU)
243
(144 MiB / 15.4% CPU)
255
(115 MiB / 3.8% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 256
(138 MiB / 5.8% CPU)
164
(142 MiB / 24.9% CPU)
🥇 256
(110 MiB / 6.9% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 272
(101 MiB / 11.5% CPU)
Not possible (no QUIC) 267
(171 MiB / 13.7% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 🥇 8,941
(91 MiB / 43.9% CPU)
516
(143 MiB / 25.0% CPU)
6,156
(136 MiB / 55.7% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 🥇 3,500
(193 MiB / 55.5% CPU)
370
(144 MiB / 24.9% CPU)
3,260
(137 MiB / 54.2% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 2,664
(150 MiB / 42.9% CPU)
Not possible (no QUIC) 2,257
(218 MiB / 51.9% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 256
(114 MiB / 2.6% CPU)
256
(127 MiB / 4.0% CPU)
🥇 256
(106 MiB / 1.9% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 250
(120 MiB / 5.4% CPU)
Not possible 🥇 256
(141 MiB / 7.5% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,500
(120 MiB / 29.6% CPU)
Not possible 4
(105 MiB / 0.2% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,028
(peak 2,640 · 114 MiB / 23.8% CPU)
Not possible 17
(105 MiB / 0.4% CPU)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 🥇 25,684
(96 MiB / 43.2% CPU)
12,522
(143 MiB / 24.6% CPU)
23,768
(90 MiB / 43.6% CPU)

Linux

Scenario Client Origin TWP nginx HAProxy Envoy YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 469
(114 MiB / 9.4% CPU)
416
(101 MiB / 9.8% CPU)
🥇 473
(84 MiB / 5.9% CPU)
472
(138 MiB / 6.1% CPU)
422
(148 MiB / 15.2% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 469
(144 MiB / 18.3% CPU)
429
(100 MiB / 17.4% CPU)
🥇 474
(82 MiB / 9.2% CPU)
471
(157 MiB / 7.3% CPU)
470
(146 MiB / 24.3% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 480
(134 MiB / 24.4% CPU)
120
(97 MiB / 3.9% CPU)
478
(88 MiB / 5.6% CPU)
2
(151 MiB / 0.1% CPU)
🥇 480
(197 MiB / 28.6% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 9,402
(134 MiB / 42.2% CPU)
0
(peak 6,896 · 102 MiB / 49.1% CPU)
🥇 10,322
(84 MiB / 40.7% CPU)
0
(peak 64 · 129 MiB / 1.9% CPU)
5,960
(171 MiB / 54.1% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 🥇 4,856
(232 MiB / 44.5% CPU)
0
(peak 2,632 · 120 MiB / 18.9% CPU)
4,798
(86 MiB / 20.8% CPU)
0
(peak 5,095 · 148 MiB / 23.5% CPU)
4,360
(166 MiB / 49.9% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 2,861
(202 MiB / 45.0% CPU)
0
(peak 446 · 117 MiB / 24.6% CPU)
1,950
(90 MiB / 34.4% CPU)
0
(127 MiB / 0.1% CPU)
2,126
(258 MiB / 47.5% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 🥇 481
(146 MiB / 14.2% CPU)
457
(79 MiB / 9.2% CPU)
472
(70 MiB / 4.8% CPU)
473
(147 MiB / 3.4% CPU)
480
(147 MiB / 12.5% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 🥇 471
(155 MiB / 12.5% CPU)
Not possible 458
(90 MiB / 8.8% CPU)
470
(153 MiB / 6.6% CPU)
470
(167 MiB / 17.9% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,725
(peak 3,366 · 150 MiB / 23.9% CPU)
Not possible 72
(peak 2,790 · 81 MiB / 0.5% CPU)
0
(peak 3,878 · 159 MiB / 20.1% CPU)
167
(164 MiB / 2.4% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,974
(145 MiB / 31.4% CPU)
Not possible 1,998
(83 MiB / 24.0% CPU)
0
(peak 2,896 · 158 MiB / 19.6% CPU)
21
(145 MiB / 0.5% CPU)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 47,808
(126 MiB / 44.9% CPU)
🥇 50,900
(101 MiB / 36.8% CPU)
48,487
(84 MiB / 39.9% CPU)
46,374
(127 MiB / 41.9% CPU)
41,253
(125 MiB / 45.3% CPU)

macOS

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

Scenario Client Origin TWP nginx HAProxy Envoy YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 97
(260 MiB / 4.1% CPU)
104
(84 MiB / 0.9% CPU)
🥇 112
(101 MiB / 1.2% CPU)
88
(146 MiB / 1.3% CPU)
96
(222 MiB / 2.8% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 87
(326 MiB / 3.8% CPU)
🥇 102
(85 MiB / 1.9% CPU)
102
(100 MiB / 1.8% CPU)
80
(154 MiB / 1.8% CPU)
102
(210 MiB / 4.6% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 112
(132 MiB / 21.5% CPU)
104
(93 MiB / 4.9% CPU)
96
(100 MiB / 8.1% CPU)
87
(158 MiB / 18.2% CPU)
107
(374 MiB / 20.1% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 2,971
(237 MiB / 27.0% CPU)
🥇 4,852
(89 MiB / 21.4% CPU)
3,724
(103 MiB / 24.9% CPU)
0
(peak 2,768 · 132 MiB / 25.0% CPU)
2,034
(219 MiB / 27.2% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 1,284
(440 MiB / 23.5% CPU)
0
(peak 3,030 · 98 MiB / 18.0% CPU)
🥇 3,848
(104 MiB / 22.4% CPU)
0
(peak 2,789 · 145 MiB / 21.4% CPU)
1,104
(180 MiB / 16.6% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 976
(310 MiB / 20.9% CPU)
0
(peak 1,350 · 112 MiB / 23.1% CPU)
🥇 1,015
(104 MiB / 29.1% CPU)
560
(131 MiB / 33.3% CPU)
607
(458 MiB / 29.6% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 104
(220 MiB / 2.4% CPU)
104
(72 MiB / 1.2% CPU)
104
(87 MiB / 1.1% CPU)
80
(145 MiB / 1.0% CPU)
🥇 111
(145 MiB / 2.1% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 🥇 112
(379 MiB / 5.2% CPU)
Not possible 112
(102 MiB / 4.2% CPU)
103
(144 MiB / 3.6% CPU)
110
(308 MiB / 7.8% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 913
(peak 1,578 · 174 MiB / 11.3% CPU)
Not possible 604
(98 MiB / 10.6% CPU)
0
(peak 2,019 · 145 MiB / 20.6% CPU)
0
(177 MiB / 1.0% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 778
(peak 1,867 · 172 MiB / 9.4% CPU)
Not possible 730
(98 MiB / 7.8% CPU)
0
(peak 2,352 · 147 MiB / 21.2% CPU)
8
(179 MiB / 1.3% CPU)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 18,181
(162 MiB / 18.4% CPU)
🥇 29,332
(86 MiB / 20.3% CPU)
16,778
(101 MiB / 20.1% CPU)
21,739
(122 MiB / 26.7% CPU)
17,478
(162 MiB / 25.8% CPU)

Slow consumer is sleep-bound; H1/H2/H3 sit in the same band. Prefer the ratio columns for early-response, duplex, and WebSocket rows.

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 @ 0386b2aa. Source: Actions 37139092496. 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,474
(84 MiB / 47.4% CPU)
8,709
(142 MiB / 24.6% CPU)
18,264
(102 MiB / 48.3% CPU)
New-connection · tiny GET 🥇 725
(84 MiB / 0.8% CPU)
248
(141 MiB / 2.2% CPU)
712
(113 MiB / 0.5% CPU)
Keep-alive · 256 KiB GET 🥇 2,948
(119 MiB / 47.1% CPU)
170
(142 MiB / 24.8% CPU)
2,709
(139 MiB / 49.1% CPU)

Linux

Median of 3 repeats @ 0386b2aa. Source: Actions 37139092496.

Workload TWP nginx HAProxy Envoy YARP
Keep-alive · tiny GET 24,036
(106 MiB / 49.4% CPU)
🥇 27,574
(99 MiB / 42.0% CPU)
26,687
(82 MiB / 43.7% CPU)
16,103
(128 MiB / 57.4% CPU)
19,910
(134 MiB / 50.3% CPU)
New-connection · tiny GET 1,143
(120 MiB / 0.5% CPU)
🥇 1,204
(103 MiB / 0.2% CPU)
1,141
(82 MiB / 0.5% CPU)
1,082
(129 MiB / 0.2% CPU)
1,141
(146 MiB)
Keep-alive · 256 KiB GET 2,814
(129 MiB / 35.2% CPU)
1,780
(100 MiB / 51.1% CPU)
🥇 3,012
(83 MiB / 32.6% CPU)
2,766
(145 MiB / 31.9% CPU)
2,247
(162 MiB / 43.9% CPU)

macOS

Median of 3 repeats on macos-15 @ 0386b2aa. Source: Actions 37139092496.

Workload TWP nginx HAProxy Envoy YARP
Keep-alive · tiny GET 8,658
(149 MiB / 20.7% CPU)
10,695
(85 MiB / 16.6% CPU)
11,290
(101 MiB / 12.2% CPU)
8,861
(122 MiB / 22.9% CPU)
🥇 12,042
(162 MiB / 22.4% CPU)
New-connection · tiny GET 110
(peak 118 · 107 MiB / 0.2% CPU)
478
(85 MiB)
🥇 651
(101 MiB)
627
(131 MiB / 0.1% CPU)
106
(peak 119 · 152 MiB / 0.2% CPU)
Keep-alive · 256 KiB GET 2,880
(214 MiB / 24.6% CPU)
2,330
(85 MiB / 16.6% CPU)
🥇 2,998
(102 MiB / 25.7% CPU)
2,874
(154 MiB / 24.8% CPU)
2,034
(182 MiB / 29.2% CPU)

Prefer TWP÷YARP in the table. Absolute RPS on GHA swings hard. 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 @ 0386b2aa — 37139283273.

OS Titanium YARP nginx HAProxy Envoy
Windows 🥇 52,937
(97 MiB / 23.1% CPU)
31,254
(120 MiB / 44.9% CPU)
0
(143 MiB / 24.8% CPU)
Not possible Not possible
Linux 🥇 36,435
(116 MiB / 29.4% CPU)
21,650
(162 MiB / 42.4% CPU)
0
(114 MiB / 24.8% CPU)
6,780
(84 MiB / 24.4% CPU)
11,826
(128 MiB / 21.5% CPU)
macOS 🥇 23,481
(147 MiB / 9.2% CPU)
13,123
(198 MiB / 13.4% CPU)
0
(85 MiB / 2.8% CPU)
0
(104 MiB / 13.3% CPU)
8,939
(120 MiB / 13.4% 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 @ 0386b2aa — 37139283273.

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

OS Titanium YARP HAProxy Envoy
Windows 🥇 55,113
(88 MiB / 23.0% CPU)
33,033
(115 MiB / 44.6% CPU)
Not possible Not possible
Linux 🥇 43,057
(123 MiB / 29.8% CPU)
24,248
(164 MiB / 43.2% CPU)
7,734
(82 MiB / 24.4% CPU)
12,392
(127 MiB / 22.4% CPU)
macOS 37,144
(158 MiB / 12.6% CPU)
🥇 50,508
(178 MiB / 37.7% CPU)
24,505
(104 MiB / 24.4% CPU)
30,920
(119 MiB / 26.1% 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). Median of 3 repeats @ 0386b2aa. Source: 37156931630.

OS Titanium YARP nginx HAProxy Envoy
Windows 🥇 22,395
(100 MiB / 44.5% CPU)
21,451
(92 MiB / 43.9% CPU)
9,638
(142 MiB / 24.8% CPU)
Not possible Not possible
Linux 24,640
(133 MiB / 44.2% CPU)
22,976
(134 MiB / 43.9% CPU)
🥇 28,478
(103 MiB / 37.0% CPU)
26,978
(85 MiB / 39.3% CPU)
27,918
(128 MiB / 38.2% CPU)
macOS 28,012
(192 MiB / 28.5% CPU)
32,133
(215 MiB / 34.9% CPU)
35,774
(85 MiB / 22.8% CPU)
🥇 36,420
(100 MiB / 33.0% CPU)
35,325
(119 MiB / 29.9% 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. Median of 3 repeats @ 0386b2aa. Source: 37156946870.

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

OS Titanium YARP HAProxy Envoy
Windows 🥇 26,080
(171 MiB / 47.0% CPU)
0
(155 MiB / 20.4% CPU)
Not possible Not possible
Linux 47,626
(197 MiB / 40.1% CPU)
0
(220 MiB / 42.2% CPU)
🥇 47,638
(85 MiB / 35.0% CPU)
0
(123 MiB / 0.1% CPU)
macOS 8,899
(238 MiB / 14.0% CPU)
0
(193 MiB / 8.9% CPU)
🥇 20,337
(100 MiB / 21.3% CPU)
0
(115 MiB / 0.2% 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 Linux only; one job per wiki row (feature arm + its baseline)
Pre-wiki smoke compare-product-smoke (Linux, repeats=1) ~30–60 min; required before full product
Release / wiki compare-product reverse via RPS suite one job per wiki row on Win/Linux/macOS; MITM arms (twp-mitm-*) on Linux only, same job as that row's reverse peers
Unary gRPC compare-grpc one job per row (H2 TLS and h2c) on Win/Linux/macOS
WebSocket dual-TLS compare-ws-h1tls one row, three OS
WebSocket RFC 8441 compare-ws-h2 one row, three OS (nginx N/A)
Heavier tables compare-bodies, post, lossy, arch, tls-cost, compare-saturation one suite run each, one job per row, on Win/Linux/macOS

See PERF-GATES.md.

Arm shards (comparison groups)

--arm-shard takes a comparison-group key (h1c-h1c, from --print-groups), not an i/n index, so a re-run measures the same row after a peer install flake. i/n remains for smoke modes. Paste unions rps-csv-<os>-shard-* (paste-compare-product-wiki.ps1 -RunIds …). Table “Source: Actions” may list several run URLs for one table — do not mix SHAs. A single-row leg allows 60 minutes of ramp and 75 minutes overall. Free GitHub accounts run 20 jobs at once, 5 of them macOS. Give a full suite a quiet window: do not push pull requests or start other macOS jobs while it runs. Re-run infrastructure failures only (gh run rerun --failed). A failed gate is a finding and is never re-rolled; publish the last attempt, never the best of several. The tables on this page are @ 0386b2aa. HTTP/3 comparison groups are extra runs on that same SHA: suite prep listed groups before libmsquic was installed, so those rows were missing from the suite matrix. Saturation, editions, and both WebSocket modes on that SHA were one job per OS. Reverse product rows, bodies, POST, lossy, arch, TLS cost, gRPC, both WebSocket modes, and saturation run on Windows, Linux, and macOS. TWP MITM arms run on Linux only. compare-editions (CLI and Plus) runs on Linux only. compare-spot on pull requests to beta and stable stays on all three OS. The historical v2 MITM note below the product tables stays @ 41f4adee because those run ids are from that fix, not this re-measure.

One wiki row = one GHA job’s Client×Origin cell set: TWP + YARP + nginx + HAProxy + Envoy (when the OS can run them) stay on the same VM. On Linux that job also runs TWP Lite and Full, so Lite÷Reverse stays a same-job ratio. Windows and macOS product jobs omit twp-mitm-*. Shards split rows, not individual proxies — so TWP÷YARP remains a same-job ratio. Do not compare absolute RPS across shards (different VMs).

Gate thresholds

  • Reverse product signal: TWP ÷ closest peer ≥ 0.50 (the peer among YARP, nginx, HAProxy, and Envoy whose sustain is nearest). Edition CLI/Plus ratios floor at 0.50.
  • MITM overhead: Lite÷Reverse ≥ 0.25 and Full÷Reverse ≥ 0.25 (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.

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