-
-
Notifications
You must be signed in to change notification settings - Fork 658
Performance
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 charts use RPC/s). Numbers are Release builds on matched GitHub-hosted runners: Windows and Linux at 4 vCPU / 16 GiB, macOS at macos-15-intel 4-core / 14 GB. Read within one chart or 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.
- 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-intel). 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 — Windows cells are Not possible.
- 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 tiny GET). With larger bodies, see Heavier reverse: at 64 KiB H2 TLS→H2 TLS, Titanium is behind YARP (~0.69–0.76×).
- Sustain = last concurrency that still met error/latency SLOs. Peak = highest RPS in that ramp.
- 🥇 = best among Titanium / nginx / YARP on Reverse rows (highest RPS; on a tie, lower memory then lower CPU%). Gold medals are per cell on tiny GET — an H2↔H2 gold 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. 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.
- Why this comparison is fair
- How to read the tables
- Measurement environment
- Windows — Titanium vs nginx vs HAProxy vs Envoy vs YARP
- Linux — Titanium vs nginx vs HAProxy vs Envoy vs YARP
- macOS — Titanium vs nginx vs HAProxy vs Envoy vs YARP
- Editions (CLI / Plus / Intercept)
- Cross-version (7.0 vs 6.0)
- Heavier reverse workloads
- Unary gRPC (H2 TLS)
- Other measurements
- Raising limits on large hosts
- Maintainer notes
All three OS use the 4-core-class public-repo GitHub-hosted runners: Windows / Linux at 4 vCPU / 16 GiB / 14 GB SSD, macOS at macos-15-intel 4-core / 14 GB (not macos-latest). 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.
| 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 |
| 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 |
| OS | macOS 15 (GitHub-hosted macos-15-intel, Intel x86_64) |
| CPU | 4 logical processors |
| RAM | 14 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-amd64 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 |
Do not use macos-latest (Apple Silicon, 3-core / 7 GB) for publishable saturation numbers.
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 @ 9a2b3a1e — 34441539402. 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).
| 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 | Sustain | Peak | % of origin-HttpClient |
|---|---|---|---|---|
| origin-direct | dotnet-httpclient |
50,319 (55 MiB / 40.4% CPU) |
50,319 (55 MiB / 40.4% CPU) |
100.0% |
| origin-direct-bombardier | bombardier |
38,565 (56 MiB / 21.8% CPU) |
38,565 (56 MiB / 21.8% CPU) |
76.6% |
| bare-reverse-http1 | dotnet-httpclient |
25,016 (58 MiB / 43.8% CPU) |
25,016 (58 MiB / 43.8% CPU) |
49.7% |
| nginx-reverse-http1 | dotnet-httpclient |
13,405 (125 MiB / 24.8% CPU) |
13,405 (125 MiB / 24.8% CPU) |
26.6% |
| yarp-reverse-http1 | dotnet-httpclient |
20,802 (87 MiB / 49.0% CPU) |
20,802 (87 MiB / 49.0% CPU) |
41.3% |
| twp-reverse-http1 | dotnet-httpclient | 🥇 24,558 (75 MiB / 48.0% CPU) |
24,558 (75 MiB / 48.0% CPU) |
48.8% |
Linux (ubuntu-latest)
| Arm | Generator | Sustain | Peak | % of origin-HttpClient |
|---|---|---|---|---|
| origin-direct | dotnet-httpclient |
102,038 (80 MiB / 44.2% CPU) |
102,038 (80 MiB / 44.2% CPU) |
100.0% |
| origin-direct-bombardier | bombardier |
61,434 (80 MiB / 37.4% CPU) |
61,434 (80 MiB / 37.4% CPU) |
60.2% |
| bare-reverse-http1 | dotnet-httpclient |
46,442 (70 MiB / 46.0% CPU) |
46,442 (70 MiB / 46.0% CPU) |
45.5% |
| nginx-reverse-http1 | dotnet-httpclient | 🥇 56,585 (76 MiB / 40.3% CPU) |
56,585 (76 MiB / 40.3% CPU) |
55.5% |
| yarp-reverse-http1 | dotnet-httpclient |
41,835 (116 MiB / 49.3% CPU) |
41,835 (116 MiB / 49.3% CPU) |
41.0% |
| twp-reverse-http1 | dotnet-httpclient |
47,721 (95 MiB / 50.9% CPU) |
47,721 (95 MiB / 50.9% CPU) |
46.8% |
Reverse peers are about 50–46% of the origin-direct HttpClient peak on this runner class (Win TWP 50.0%, Lin TWP 45.6%). Prefer the % column over absolute RPS across runs. Bare and origin-direct are controls (not medal peers).
Peer ratios (÷YARP / ÷nginx) on median peak; RPS cells embed (MiB / CPU%).
Windows (windows-latest)
| Arm | Generator | Sustain | Peak | ÷YARP | ÷nginx |
|---|---|---|---|---|---|
| nginx-reverse-http2 | dotnet-httpclient |
7,992 (141 MiB / 24.6% CPU) |
7,992 (141 MiB / 24.6% CPU) |
0.28× | 1.00× |
| yarp-reverse-http2 | dotnet-httpclient |
28,294 (97 MiB / 54.9% CPU) |
28,294 (97 MiB / 54.9% CPU) |
1.00× | 3.54× |
| twp-reverse-http2-cleartext | dotnet-httpclient | 🥇 33,720 (105 MiB / 52.2% CPU) |
33,720 (105 MiB / 52.2% CPU) |
1.19× | 4.22× |
Linux (ubuntu-latest)
| Arm | Generator | Sustain | Peak | ÷YARP | ÷nginx |
|---|---|---|---|---|---|
| nginx-reverse-http2 | dotnet-httpclient |
23,276 (102 MiB / 18.9% CPU) |
23,276 (102 MiB / 18.9% CPU) |
0.53× | 1.00× |
| yarp-reverse-http2 | dotnet-httpclient |
44,299 (122 MiB / 48.0% CPU) |
44,299 (122 MiB / 48.0% CPU) |
1.00× | 1.90× |
| twp-reverse-http2-cleartext | dotnet-httpclient | 🥇 49,167 (124 MiB / 52.1% CPU) |
49,167 (124 MiB / 52.1% CPU) |
1.11× | 2.11× |
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.
Windows (windows-latest)
| Arm | Generator | Sustain | Peak | ÷YARP | ÷nginx |
|---|---|---|---|---|---|
| nginx-reverse-http3-cleartext | dotnet-httpclient | Not possible (no QUIC) | Not possible (no QUIC) | — | — |
| yarp-reverse-http3-cleartext | dotnet-httpclient |
14,041 (142 MiB / 51.8% CPU) |
14,041 (142 MiB / 51.8% CPU) |
1.00× | — |
| twp-reverse-http3-cleartext | dotnet-httpclient | 🥇 14,348 (104 MiB / 43.8% CPU) |
14,348 (104 MiB / 43.8% CPU) |
1.02× | — |
Linux (ubuntu-latest)
| Arm | Generator | Sustain | Peak | ÷YARP | ÷nginx |
|---|---|---|---|---|---|
| nginx-reverse-http3-cleartext | dotnet-httpclient |
0 (110 MiB / 21.8% CPU) |
24,928 (110 MiB / 21.8% CPU) |
0.89× | 1.00× |
| yarp-reverse-http3-cleartext | dotnet-httpclient |
27,936 (195 MiB / 48.8% CPU) |
27,936 (195 MiB / 48.8% CPU) |
1.00× | 1.12× |
| twp-reverse-http3-cleartext | dotnet-httpclient | 🥇 30,636 (159 MiB / 52.7% CPU) |
30,636 (159 MiB / 52.7% CPU) |
1.10× | 1.23× |
Client / origin: HTTP version and whether TLS is used (plain = cleartext, TLS = encrypted, QUIC = HTTP/3).
Median of 3 repeats on windows-latest (4 vCPU / 16 GiB). Bare reverse 5×5 @ 9a2b3a1e — compare-product 34441526151. 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 sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · plain | HTTP/1 · plain | 🥇 23134 (75 MiB / 47.5% CPU) |
🥇 23134 (75 MiB / 47.5% CPU) |
13916 (125 MiB / 24.9% CPU) |
13916 (125 MiB / 24.9% CPU) |
Not possible | Not possible | Not possible | Not possible |
21699 (86 MiB / 48.8% CPU) |
21699 (86 MiB / 48.8% CPU) |
| HTTP/1 · plain | HTTP/1 · TLS | 🥇 20749 (89 MiB / 52.8% CPU) |
🥇 20749 (89 MiB / 52.8% CPU) |
8366 (135 MiB / 24.6% CPU) |
8366 (135 MiB / 24.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
18784 (100 MiB / 49% CPU) |
18784 (100 MiB / 49% CPU) |
| HTTP/1 · plain | HTTP/2 · plain | 🥇 45733 (115 MiB / 50.5% CPU) |
🥇 45733 (115 MiB / 50.5% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
39820 (90 MiB / 49.2% CPU) |
39820 (90 MiB / 49.2% CPU) |
| HTTP/1 · plain | HTTP/2 · TLS | 🥇 32162 (120 MiB / 46.7% CPU) |
🥇 32162 (120 MiB / 46.7% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
29670 (98 MiB / 49.7% CPU) |
29670 (98 MiB / 49.7% CPU) |
| HTTP/1 · plain | HTTP/3 · QUIC | 🥇 18315 (106 MiB / 50.6% CPU) |
🥇 18315 (106 MiB / 50.6% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not possible | Not possible | Not possible | Not possible |
17823 (117 MiB / 51% CPU) |
17823 (117 MiB / 51% CPU) |
| HTTP/1 · TLS | HTTP/1 · plain | 🥇 25720 (90 MiB / 48.4% CPU) |
🥇 25720 (90 MiB / 48.4% CPU) |
12987 (141 MiB / 24.8% CPU) |
12987 (141 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
22690 (102 MiB / 48.2% CPU) |
22690 (102 MiB / 48.2% CPU) |
| HTTP/1 · TLS | HTTP/1 · TLS | 🥇 18942 (91 MiB / 48.6% CPU) |
🥇 18942 (91 MiB / 48.6% CPU) |
7218 (143 MiB / 24.8% CPU) |
7218 (143 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
17296 (104 MiB / 49.7% CPU) |
17296 (104 MiB / 49.7% CPU) |
| HTTP/1 · TLS | HTTP/2 · plain | 🥇 27614 (111 MiB / 44.5% CPU) |
🥇 27614 (111 MiB / 44.5% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
26015 (104 MiB / 47.6% CPU) |
26015 (104 MiB / 47.6% CPU) |
| HTTP/1 · TLS | HTTP/2 · TLS | 🥇 32132 (117 MiB / 45.4% CPU) |
🥇 32132 (117 MiB / 45.4% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
29999 (103 MiB / 48.2% CPU) |
29999 (103 MiB / 48.2% CPU) |
| HTTP/1 · TLS | HTTP/3 · QUIC | 🥇 16275 (110 MiB / 51.3% CPU) |
🥇 16275 (110 MiB / 51.3% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not possible | Not possible | Not possible | Not possible |
15797 (124 MiB / 51.4% CPU) |
15797 (124 MiB / 51.4% CPU) |
| HTTP/2 · plain | HTTP/1 · plain | 🥇 34933 (89 MiB / 55% CPU) |
🥇 34933 (89 MiB / 55% CPU) |
9362 (127 MiB / 24.4% CPU) |
9362 (127 MiB / 24.4% CPU) |
Not possible | Not possible | Not possible | Not possible |
31625 (86 MiB / 54.4% CPU) |
31625 (86 MiB / 54.4% CPU) |
| HTTP/2 · plain | HTTP/1 · TLS | 🥇 35086 (105 MiB / 49% CPU) |
🥇 35086 (105 MiB / 49% CPU) |
8849 (138 MiB / 24.9% CPU) |
8849 (138 MiB / 24.9% CPU) |
Not possible | Not possible | Not possible | Not possible |
32740 (92 MiB / 50.1% CPU) |
32740 (92 MiB / 50.1% CPU) |
| HTTP/2 · plain | HTTP/2 · plain | 🥇 112638 (61 MiB / 28.3% CPU) |
🥇 112638 (61 MiB / 28.3% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
64290 (90 MiB / 49.6% CPU) |
64290 (90 MiB / 49.6% CPU) |
| HTTP/2 · plain | HTTP/2 · TLS | 🥇 70105 (62 MiB / 20.1% CPU) |
🥇 70105 (62 MiB / 20.1% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
43971 (99 MiB / 35.9% CPU) |
43971 (99 MiB / 35.9% CPU) |
| HTTP/2 · plain | HTTP/3 · QUIC | 🥇 33088 (128 MiB / 53.1% CPU) |
🥇 33088 (128 MiB / 53.1% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not possible | Not possible | Not possible | Not possible |
31406 (130 MiB / 52% CPU) |
31406 (130 MiB / 52% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain | 🥇 34943 (97 MiB / 52.2% CPU) |
🥇 34943 (97 MiB / 52.2% CPU) |
8430 (141 MiB / 24.8% CPU) |
8430 (141 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
29441 (96 MiB / 53.8% CPU) |
29441 (96 MiB / 53.8% CPU) |
| HTTP/2 · TLS | HTTP/1 · TLS | 🥇 29487 (97 MiB / 53.6% CPU) |
🥇 29487 (97 MiB / 53.6% CPU) |
6542 (144 MiB / 24.6% CPU) |
6542 (144 MiB / 24.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
25117 (98 MiB / 54.2% CPU) |
25117 (98 MiB / 54.2% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain | 🥇 107152 (75 MiB / 29.1% CPU) |
🥇 107152 (75 MiB / 29.1% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
59098 (102 MiB / 52.7% CPU) |
59098 (102 MiB / 52.7% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS | 🥇 84487 (73 MiB / 28.8% CPU) |
🥇 84487 (73 MiB / 28.8% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
49546 (98 MiB / 49% CPU) |
49546 (98 MiB / 49% CPU) |
| HTTP/2 · TLS | HTTP/3 · QUIC | 🥇 30034 (127 MiB / 53.4% CPU) |
🥇 30034 (127 MiB / 53.4% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not possible | Not possible | Not possible | Not possible |
25937 (123 MiB / 51.5% CPU) |
25937 (123 MiB / 51.5% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain | 🥇 19348 (107 MiB / 44% CPU) |
🥇 19348 (107 MiB / 44% CPU) |
Not measured | Not measured | Not possible | Not possible | Not possible | Not possible |
18041 (143 MiB / 50.9% CPU) |
18041 (143 MiB / 50.9% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 13646 (113 MiB / 46.7% CPU) |
🥇 13646 (113 MiB / 46.7% CPU) |
Not measured | Not measured | Not possible | Not possible | Not possible | Not possible |
13437 (145 MiB / 52.4% CPU) |
13437 (145 MiB / 52.4% CPU) |
| HTTP/3 · QUIC | HTTP/2 · plain | 🥇 34128 (136 MiB / 47.6% CPU) |
🥇 34128 (136 MiB / 47.6% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
25450 (166 MiB / 50.1% CPU) |
25450 (166 MiB / 50.1% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS | 🥇 33850 (138 MiB / 48% CPU) |
🥇 33850 (138 MiB / 48% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not possible | Not possible | Not possible | Not possible |
26828 (168 MiB / 48.5% CPU) |
26828 (168 MiB / 48.5% CPU) |
| HTTP/3 · QUIC | HTTP/3 · QUIC | 🥇 23150 (121 MiB / 41.8% CPU) |
🥇 23150 (121 MiB / 41.8% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not possible | Not possible | Not possible | Not possible |
12064 (167 MiB / 53.1% CPU) |
12064 (167 MiB / 53.1% CPU) |
Same Client×Origin wires with interception on (compare-product 34441526151). 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.50× and Full ≥ 0.50× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.70× (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. @ 9a2b3a1e: 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 |
25054 (82 MiB / 50.4% CPU) |
24740 (82 MiB / 51.6% CPU) |
1.08× | 1.07× |
| HTTP/1 · plain | HTTP/1 · TLS |
20351 (95 MiB / 54.2% CPU) |
19992 (95 MiB / 52% CPU) |
0.98× | 0.96× |
| HTTP/1 · plain | HTTP/2 · plain |
45468 (110 MiB / 51.5% CPU) |
43600 (120 MiB / 49.4% CPU) |
0.99× | 0.95× |
| HTTP/1 · plain | HTTP/2 · TLS |
32619 (118 MiB / 46.8% CPU) |
31475 (125 MiB / 46.3% CPU) |
1.01× | 0.98× |
| HTTP/1 · plain | HTTP/3 · QUIC |
17858 (105 MiB / 47.7% CPU) |
17488 (104 MiB / 52.6% CPU) |
0.98× | 0.95× |
| HTTP/1 · TLS | HTTP/1 · plain |
24388 (98 MiB / 47.9% CPU) |
24773 (95 MiB / 48.2% CPU) |
0.95× | 0.96× |
| HTTP/1 · TLS | HTTP/1 · TLS |
18766 (94 MiB / 49.4% CPU) |
18334 (96 MiB / 49.4% CPU) |
0.99× | 0.97× |
| HTTP/1 · TLS | HTTP/2 · plain |
27503 (112 MiB / 47.3% CPU) |
27245 (112 MiB / 46.1% CPU) |
1× | 0.99× |
| HTTP/1 · TLS | HTTP/2 · TLS |
31677 (118 MiB / 47.9% CPU) |
30700 (117 MiB / 44.2% CPU) |
0.99× | 0.96× |
| HTTP/1 · TLS | HTTP/3 · QUIC |
15862 (113 MiB / 50.9% CPU) |
15726 (112 MiB / 50.8% CPU) |
0.97× | 0.97× |
| HTTP/2 · plain | HTTP/1 · plain |
34123 (95 MiB / 55.4% CPU) |
33304 (96 MiB / 56.8% CPU) |
0.98× | 0.95× |
| HTTP/2 · plain | HTTP/1 · TLS |
34643 (106 MiB / 50.5% CPU) |
34092 (109 MiB / 52.9% CPU) |
0.99× | 0.97× |
| HTTP/2 · plain | HTTP/2 · plain |
86231 (71 MiB / 41.3% CPU) |
80246 (73 MiB / 41.9% CPU) |
0.77× | 0.71× |
| HTTP/2 · plain | HTTP/2 · TLS |
72044 (77 MiB / 36.5% CPU) |
67973 (77 MiB / 39.7% CPU) |
1.03× | 0.97× |
| HTTP/2 · plain | HTTP/3 · QUIC |
33179 (124 MiB / 53.4% CPU) |
32949 (129 MiB / 53.1% CPU) |
1× | 1× |
| HTTP/2 · TLS | HTTP/1 · plain |
33820 (98 MiB / 55.2% CPU) |
32652 (107 MiB / 54.9% CPU) |
0.97× | 0.93× |
| HTTP/2 · TLS | HTTP/1 · TLS |
28427 (100 MiB / 56.2% CPU) |
27770 (105 MiB / 53.2% CPU) |
0.96× | 0.94× |
| HTTP/2 · TLS | HTTP/2 · plain |
84248 (86 MiB / 40.5% CPU) |
80554 (84 MiB / 39.7% CPU) |
0.79× | 0.75× |
| HTTP/2 · TLS | HTTP/2 · TLS |
68743 (86 MiB / 38.1% CPU) |
65184 (81 MiB / 39.6% CPU) |
0.81× | 0.77× |
| HTTP/2 · TLS | HTTP/3 · QUIC |
29843 (126 MiB / 54.4% CPU) |
29276 (133 MiB / 53.5% CPU) |
0.99× | 0.97× |
| HTTP/3 · QUIC | HTTP/1 · plain |
18648 (117 MiB / 44.9% CPU) |
17458 (114 MiB / 44.7% CPU) |
0.96× | 0.9× |
| HTTP/3 · QUIC | HTTP/1 · TLS |
12964 (123 MiB / 46.8% CPU) |
12392 (116 MiB / 47.5% CPU) |
0.95× | 0.91× |
| HTTP/3 · QUIC | HTTP/2 · plain |
30904 (132 MiB / 48.6% CPU) |
29726 (133 MiB / 50.6% CPU) |
0.91× | 0.87× |
| HTTP/3 · QUIC | HTTP/2 · TLS |
31489 (143 MiB / 48.5% CPU) |
30510 (143 MiB / 48.3% CPU) |
0.93× | 0.9× |
| HTTP/3 · QUIC | HTTP/3 · QUIC |
16061 (120 MiB / 49.2% CPU) |
15312 (119 MiB / 49.5% CPU) |
0.69× | 0.66× |
Median of 3 repeats on ubuntu-latest (4 vCPU / 16 GiB). Bare reverse 5×5 @ 9a2b3a1e — compare-product 34441526151. 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 sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · plain | HTTP/1 · plain |
36672 (94 MiB / 50.5% CPU) |
36672 (94 MiB / 50.5% CPU) |
🥇 44112 (76 MiB / 40.4% CPU) |
🥇 44112 (76 MiB / 40.4% CPU) |
41748 (67 MiB / 41.3% CPU) |
41748 (67 MiB / 41.3% CPU) |
24415 (116 MiB / 59.5% CPU) |
24415 (116 MiB / 59.5% CPU) |
32393 (115 MiB / 48.8% CPU) |
32393 (115 MiB / 48.8% CPU) |
| HTTP/1 · plain | HTTP/1 · TLS |
28506 (107 MiB / 49.3% CPU) |
28506 (107 MiB / 49.3% CPU) |
🥇 34687 (93 MiB / 40.7% CPU) |
🥇 34687 (93 MiB / 40.7% CPU) |
32839 (71 MiB / 43% CPU) |
32839 (71 MiB / 43% CPU) |
22383 (117 MiB / 56.4% CPU) |
22383 (117 MiB / 56.4% CPU) |
25710 (138 MiB / 49.6% CPU) |
25710 (138 MiB / 49.6% CPU) |
| HTTP/1 · plain | HTTP/2 · plain | 🥇 45673 (129 MiB / 52.7% CPU) |
🥇 45673 (129 MiB / 52.7% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
26830 (68 MiB / 42.8% CPU) |
26830 (68 MiB / 42.8% CPU) |
27447 (116 MiB / 61% CPU) |
27447 (116 MiB / 61% CPU) |
40965 (125 MiB / 49.4% CPU) |
40965 (125 MiB / 49.4% CPU) |
| HTTP/1 · plain | HTTP/2 · TLS | 🥇 37934 (147 MiB / 49.1% CPU) |
🥇 37934 (147 MiB / 49.1% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
35699 (68 MiB / 43% CPU) |
35699 (68 MiB / 43% CPU) |
19949 (117 MiB / 61.9% CPU) |
19949 (117 MiB / 61.9% CPU) |
35520 (132 MiB / 47.3% CPU) |
35520 (132 MiB / 47.3% CPU) |
| HTTP/1 · plain | HTTP/3 · QUIC | 🥇 26151 (141 MiB / 53.8% CPU) |
🥇 26151 (141 MiB / 53.8% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
12126 (120 MiB / 56.3% CPU) |
12126 (120 MiB / 56.3% CPU) |
24086 (155 MiB / 48.3% CPU) |
24086 (155 MiB / 48.3% CPU) |
| HTTP/1 · TLS | HTTP/1 · plain |
27742 (111 MiB / 48.9% CPU) |
27742 (111 MiB / 48.9% CPU) |
33232 (100 MiB / 41.5% CPU) |
33232 (100 MiB / 41.5% CPU) |
🥇 33283 (85 MiB / 43.6% CPU) |
🥇 33283 (85 MiB / 43.6% CPU) |
21660 (127 MiB / 56.4% CPU) |
21660 (127 MiB / 56.4% CPU) |
24735 (142 MiB / 49.4% CPU) |
24735 (142 MiB / 49.4% CPU) |
| HTTP/1 · TLS | HTTP/1 · TLS |
23878 (111 MiB / 48.2% CPU) |
23878 (111 MiB / 48.2% CPU) |
🥇 28042 (103 MiB / 41.6% CPU) |
🥇 28042 (103 MiB / 41.6% CPU) |
27394 (86 MiB / 43.8% CPU) |
27394 (86 MiB / 43.8% CPU) |
19309 (128 MiB / 55% CPU) |
19309 (128 MiB / 55% CPU) |
21716 (142 MiB / 49.4% CPU) |
21716 (142 MiB / 49.4% CPU) |
| HTTP/1 · TLS | HTTP/2 · plain | 🥇 34010 (140 MiB / 51.4% CPU) |
🥇 34010 (140 MiB / 51.4% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
21760 (83 MiB / 42.9% CPU) |
21760 (83 MiB / 42.9% CPU) |
18208 (127 MiB / 58.6% CPU) |
18208 (127 MiB / 58.6% CPU) |
30979 (141 MiB / 49.2% CPU) |
30979 (141 MiB / 49.2% CPU) |
| HTTP/1 · TLS | HTTP/2 · TLS | 🥇 29656 (157 MiB / 48.6% CPU) |
🥇 29656 (157 MiB / 48.6% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
29078 (83 MiB / 44% CPU) |
29078 (83 MiB / 44% CPU) |
21903 (126 MiB / 56.9% CPU) |
21903 (126 MiB / 56.9% CPU) |
27210 (146 MiB / 48.3% CPU) |
27210 (146 MiB / 48.3% CPU) |
| HTTP/1 · TLS | HTTP/3 · QUIC | 🥇 22269 (152 MiB / 52.9% CPU) |
🥇 22269 (152 MiB / 52.9% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
11093 (130 MiB / 55.9% CPU) |
11093 (130 MiB / 55.9% CPU) |
19851 (166 MiB / 49.4% CPU) |
19851 (166 MiB / 49.4% CPU) |
| HTTP/2 · plain | HTTP/1 · plain | 🥇 41263 (126 MiB / 53.1% CPU) |
🥇 41263 (126 MiB / 53.1% CPU) |
18170 (79 MiB / 19.6% CPU) |
18170 (79 MiB / 19.6% CPU) |
27652 (69 MiB / 24.4% CPU) |
27652 (69 MiB / 24.4% CPU) |
17979 (118 MiB / 23% CPU) |
17979 (118 MiB / 23% CPU) |
39219 (115 MiB / 48.3% CPU) |
39219 (115 MiB / 48.3% CPU) |
| HTTP/2 · plain | HTTP/1 · TLS | 🥇 32750 (131 MiB / 51% CPU) |
🥇 32750 (131 MiB / 51% CPU) |
15078 (100 MiB / 19.2% CPU) |
15078 (100 MiB / 19.2% CPU) |
22285 (71 MiB / 24.5% CPU) |
22285 (71 MiB / 24.5% CPU) |
16529 (119 MiB / 23.1% CPU) |
16529 (119 MiB / 23.1% CPU) |
31159 (125 MiB / 48.4% CPU) |
31159 (125 MiB / 48.4% CPU) |
| HTTP/2 · plain | HTTP/2 · plain | 🥇 97239 (90 MiB / 38% CPU) |
🥇 97239 (90 MiB / 38% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
30261 (69 MiB / 24.3% CPU) |
30261 (69 MiB / 24.3% CPU) |
24868 (116 MiB / 21.8% CPU) |
24868 (116 MiB / 21.8% CPU) |
55752 (122 MiB / 47.1% CPU) |
55752 (122 MiB / 47.1% CPU) |
| HTTP/2 · plain | HTTP/2 · TLS | 🥇 67702 (93 MiB / 38.2% CPU) |
🥇 67702 (93 MiB / 38.2% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
23815 (68 MiB / 24.4% CPU) |
23815 (68 MiB / 24.4% CPU) |
16231 (118 MiB / 21.5% CPU) |
16231 (118 MiB / 21.5% CPU) |
44692 (129 MiB / 45.3% CPU) |
44692 (129 MiB / 45.3% CPU) |
| HTTP/2 · plain | HTTP/3 · QUIC | 🥇 30188 (150 MiB / 51.4% CPU) |
🥇 30188 (150 MiB / 51.4% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
5340 (121 MiB / 24.8% CPU) |
5340 (121 MiB / 24.8% CPU) |
28445 (156 MiB / 45.5% CPU) |
28445 (156 MiB / 45.5% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain | 🥇 38662 (128 MiB / 52.3% CPU) |
🥇 38662 (128 MiB / 52.3% CPU) |
17745 (100 MiB / 18.8% CPU) |
17745 (100 MiB / 18.8% CPU) |
24393 (81 MiB / 24.3% CPU) |
24393 (81 MiB / 24.3% CPU) |
17919 (128 MiB / 22.5% CPU) |
17919 (128 MiB / 22.5% CPU) |
34084 (122 MiB / 48.1% CPU) |
34084 (122 MiB / 48.1% CPU) |
| HTTP/2 · TLS | HTTP/1 · TLS | 🥇 30318 (119 MiB / 49.8% CPU) |
🥇 30318 (119 MiB / 49.8% CPU) |
15083 (110 MiB / 19.6% CPU) |
15083 (110 MiB / 19.6% CPU) |
20646 (85 MiB / 24.4% CPU) |
20646 (85 MiB / 24.4% CPU) |
16394 (128 MiB / 22.2% CPU) |
16394 (128 MiB / 22.2% CPU) |
27788 (131 MiB / 48.5% CPU) |
27788 (131 MiB / 48.5% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain | 🥇 85544 (102 MiB / 38% CPU) |
🥇 85544 (102 MiB / 38% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
53648 (85 MiB / 24.2% CPU) |
53648 (85 MiB / 24.2% CPU) |
24277 (126 MiB / 21.5% CPU) |
24277 (126 MiB / 21.5% CPU) |
44751 (125 MiB / 46.4% CPU) |
44751 (125 MiB / 46.4% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS | 🥇 63379 (99 MiB / 36% CPU) |
🥇 63379 (99 MiB / 36% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
22388 (84 MiB / 24.4% CPU) |
22388 (84 MiB / 24.4% CPU) |
21510 (125 MiB / 20.6% CPU) |
21510 (125 MiB / 20.6% CPU) |
38643 (128 MiB / 45.3% CPU) |
38643 (128 MiB / 45.3% CPU) |
| HTTP/2 · TLS | HTTP/3 · QUIC | 🥇 28423 (151 MiB / 50.5% CPU) |
🥇 28423 (151 MiB / 50.5% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
5476 (129 MiB / 24.6% CPU) |
5476 (129 MiB / 24.6% CPU) |
25085 (163 MiB / 45.8% CPU) |
25085 (163 MiB / 45.8% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain |
23715 (149 MiB / 52.2% CPU) |
23715 (149 MiB / 52.2% CPU) |
0 (107 MiB / 21.6% CPU) |
19206 (107 MiB / 21.6% CPU) |
🥇 30835 (86 MiB / 25.2% CPU) |
🥇 30835 (86 MiB / 25.2% CPU) |
3 (130 MiB / 0.1% CPU) |
3 (130 MiB / 0.1% CPU) |
21546 (186 MiB / 49% CPU) |
21546 (186 MiB / 49% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 19505 (160 MiB / 49.3% CPU) |
🥇 19505 (160 MiB / 49.3% CPU) |
0 (117 MiB / 22.7% CPU) |
14850 (117 MiB / 22.7% CPU) |
Not measured | Not measured | Not measured | Not measured |
18423 (197 MiB / 49.9% CPU) |
18423 (197 MiB / 49.9% CPU) |
| HTTP/3 · QUIC | HTTP/2 · plain | 🥇 31386 (160 MiB / 56.2% CPU) |
🥇 31386 (160 MiB / 56.2% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
29864 (86 MiB / 25.4% CPU) |
29864 (86 MiB / 25.4% CPU) |
Not measured | Not measured |
26924 (197 MiB / 48% CPU) |
26924 (197 MiB / 48% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS | 🥇 27669 (154 MiB / 52.3% CPU) |
🥇 27669 (154 MiB / 52.3% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | Not measured | Not measured | Not measured | Not measured |
24488 (199 MiB / 47.6% CPU) |
24488 (199 MiB / 47.6% CPU) |
| HTTP/3 · QUIC | HTTP/3 · QUIC | 🥇 21858 (164 MiB / 47.9% CPU) |
🥇 21858 (164 MiB / 47.9% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured | Not measured | Not measured |
17840 (209 MiB / 47% CPU) |
17840 (209 MiB / 47% CPU) |
Same Client×Origin wires with interception on (compare-product 34441526151). 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.50× and Full ≥ 0.50× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.70× (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. @ 9a2b3a1e: 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 |
35578 (88 MiB / 50.3% CPU) |
34914 (97 MiB / 50.4% CPU) |
0.97× | 0.95× |
| HTTP/1 · plain | HTTP/1 · TLS |
28326 (114 MiB / 49.8% CPU) |
28227 (114 MiB / 49.8% CPU) |
0.99× | 0.99× |
| HTTP/1 · plain | HTTP/2 · plain |
46274 (124 MiB / 54.5% CPU) |
44157 (135 MiB / 54.7% CPU) |
1.01× | 0.97× |
| HTTP/1 · plain | HTTP/2 · TLS |
37133 (147 MiB / 51% CPU) |
36654 (146 MiB / 51.6% CPU) |
0.98× | 0.97× |
| HTTP/1 · plain | HTTP/3 · QUIC |
26091 (142 MiB / 54.6% CPU) |
25394 (145 MiB / 54.4% CPU) |
1× | 0.97× |
| HTTP/1 · TLS | HTTP/1 · plain |
27963 (116 MiB / 50.4% CPU) |
27273 (118 MiB / 50% CPU) |
1.01× | 0.98× |
| HTTP/1 · TLS | HTTP/1 · TLS |
24331 (118 MiB / 49% CPU) |
23589 (116 MiB / 48.9% CPU) |
1.02× | 0.99× |
| HTTP/1 · TLS | HTTP/2 · plain |
33674 (144 MiB / 52.4% CPU) |
32894 (144 MiB / 52% CPU) |
0.99× | 0.97× |
| HTTP/1 · TLS | HTTP/2 · TLS |
29341 (163 MiB / 49.4% CPU) |
29024 (160 MiB / 50.3% CPU) |
0.99× | 0.98× |
| HTTP/1 · TLS | HTTP/3 · QUIC |
21874 (159 MiB / 53.2% CPU) |
21237 (162 MiB / 53.3% CPU) |
0.98× | 0.95× |
| HTTP/2 · plain | HTTP/1 · plain |
39834 (120 MiB / 54% CPU) |
38577 (118 MiB / 53.7% CPU) |
0.97× | 0.93× |
| HTTP/2 · plain | HTTP/1 · TLS |
32663 (135 MiB / 52.2% CPU) |
31085 (123 MiB / 51.7% CPU) |
1× | 0.95× |
| HTTP/2 · plain | HTTP/2 · plain |
73656 (93 MiB / 43.9% CPU) |
71161 (97 MiB / 42.4% CPU) |
0.76× | 0.73× |
| HTTP/2 · plain | HTTP/2 · TLS |
55089 (105 MiB / 41.2% CPU) |
51723 (99 MiB / 40.6% CPU) |
0.81× | 0.76× |
| HTTP/2 · plain | HTTP/3 · QUIC |
30054 (151 MiB / 51.4% CPU) |
29788 (156 MiB / 51.7% CPU) |
1× | 0.99× |
| HTTP/2 · TLS | HTTP/1 · plain |
36903 (123 MiB / 53.2% CPU) |
35802 (127 MiB / 53.5% CPU) |
0.95× | 0.93× |
| HTTP/2 · TLS | HTTP/1 · TLS |
30528 (130 MiB / 51.2% CPU) |
29484 (124 MiB / 50.3% CPU) |
1.01× | 0.97× |
| HTTP/2 · TLS | HTTP/2 · plain |
63418 (114 MiB / 43% CPU) |
60875 (111 MiB / 42.8% CPU) |
0.74× | 0.71× |
| HTTP/2 · TLS | HTTP/2 · TLS |
52660 (106 MiB / 40.8% CPU) |
49767 (106 MiB / 39.5% CPU) |
0.83× | 0.79× |
| HTTP/2 · TLS | HTTP/3 · QUIC |
28301 (166 MiB / 51.1% CPU) |
27450 (154 MiB / 50.7% CPU) |
1× | 0.97× |
| HTTP/3 · QUIC | HTTP/1 · plain |
22951 (150 MiB / 52.3% CPU) |
22840 (150 MiB / 52.4% CPU) |
0.97× | 0.96× |
| HTTP/3 · QUIC | HTTP/1 · TLS |
18924 (165 MiB / 50.9% CPU) |
18029 (157 MiB / 49.7% CPU) |
0.97× | 0.92× |
| HTTP/3 · QUIC | HTTP/2 · plain |
30229 (167 MiB / 57.6% CPU) |
29277 (163 MiB / 56.6% CPU) |
0.96× | 0.93× |
| HTTP/3 · QUIC | HTTP/2 · TLS |
26262 (163 MiB / 53.8% CPU) |
25788 (168 MiB / 53.4% CPU) |
0.95× | 0.93× |
| HTTP/3 · QUIC | HTTP/3 · QUIC |
20948 (163 MiB / 50.8% CPU) |
20826 (162 MiB / 50.9% CPU) |
0.96× | 0.95× |
Median of 3 repeats on macos-15-intel (4-core / 14 GB). Bare reverse 5×5 @ 9a2b3a1e — compare-product 34441526151. 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-amd64 1.36.7), Homebrew libmsquic (+ DYLD_*), and YARP. Do not publish from macos-latest (3-core / 7 GB). 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 sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · plain | HTTP/1 · plain | 🥇 12131 (82 MiB / 32.8% CPU) |
🥇 12131 (82 MiB / 32.8% CPU) |
6938 (52 MiB / 11.6% CPU) |
6938 (52 MiB / 11.6% CPU) |
10945 (48 MiB / 23.2% CPU) |
10945 (48 MiB / 23.2% CPU) |
3332 (72 MiB / 22.4% CPU) |
3332 (72 MiB / 22.4% CPU) |
10805 (105 MiB / 33.6% CPU) |
10805 (105 MiB / 33.6% CPU) |
| HTTP/1 · plain | HTTP/1 · TLS | 🥇 11064 (93 MiB / 36.5% CPU) |
🥇 11064 (93 MiB / 36.5% CPU) |
5152 (73 MiB / 17.4% CPU) |
5152 (73 MiB / 17.4% CPU) |
7159 (53 MiB / 25.9% CPU) |
7159 (53 MiB / 25.9% CPU) |
0 (75 MiB / 21% CPU) |
2011 (75 MiB / 21% CPU) |
9146 (129 MiB / 36.8% CPU) |
9146 (129 MiB / 36.8% CPU) |
| HTTP/1 · plain | HTTP/2 · plain | 🥇 19185 (88 MiB / 33.8% CPU) |
🥇 19185 (88 MiB / 33.8% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
11853 (50 MiB / 25.6% CPU) |
11853 (50 MiB / 25.6% CPU) |
6000 (73 MiB / 36.6% CPU) |
6000 (73 MiB / 36.6% CPU) |
18729 (111 MiB / 34% CPU) |
18729 (111 MiB / 34% CPU) |
| HTTP/1 · plain | HTTP/2 · TLS |
9927 (129 MiB / 32.9% CPU) |
9927 (129 MiB / 32.9% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
8770 (51 MiB / 26.5% CPU) |
8770 (51 MiB / 26.5% CPU) |
5948 (73 MiB / 34.9% CPU) |
5948 (73 MiB / 34.9% CPU) |
🥇 13674 (116 MiB / 32.9% CPU) |
🥇 13674 (116 MiB / 32.9% CPU) |
| HTTP/1 · plain | HTTP/3 · QUIC |
3662 (90 MiB / 45.3% CPU) |
3662 (90 MiB / 45.3% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
3393 (75 MiB / 38.2% CPU) |
2631 (75 MiB / 38.2% CPU) |
🥇 5774 (116 MiB / 33.4% CPU) |
🥇 5774 (116 MiB / 33.4% CPU) |
| HTTP/1 · TLS | HTTP/1 · plain | 🥇 14162 (94 MiB / 31.5% CPU) |
🥇 14162 (94 MiB / 31.5% CPU) |
8184 (74 MiB / 18.3% CPU) |
8184 (74 MiB / 18.3% CPU) |
9938 (64 MiB / 25.6% CPU) |
9938 (64 MiB / 25.6% CPU) |
4084 (79 MiB / 37.4% CPU) |
4328 (79 MiB / 37.4% CPU) |
7647 (115 MiB / 32.3% CPU) |
7647 (115 MiB / 32.3% CPU) |
| HTTP/1 · TLS | HTTP/1 · TLS |
6128 (97 MiB / 31.7% CPU) |
6128 (97 MiB / 31.7% CPU) |
4903 (81 MiB / 15.5% CPU) |
4903 (81 MiB / 15.5% CPU) |
🥇 9241 (67 MiB / 28% CPU) |
🥇 9241 (67 MiB / 28% CPU) |
3594 (80 MiB / 38.9% CPU) |
3594 (80 MiB / 38.9% CPU) |
6601 (172 MiB / 35% CPU) |
6601 (172 MiB / 35% CPU) |
| HTTP/1 · TLS | HTTP/2 · plain |
8317 (96 MiB / 32.8% CPU) |
8317 (96 MiB / 32.8% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | 🥇 12353 (66 MiB / 28% CPU) |
🥇 12353 (66 MiB / 28% CPU) |
5378 (80 MiB / 20.9% CPU) |
3107 (80 MiB / 20.9% CPU) |
10735 (122 MiB / 32.2% CPU) |
10735 (122 MiB / 32.2% CPU) |
| HTTP/1 · TLS | HTTP/2 · TLS |
8457 (161 MiB / 33.8% CPU) |
8457 (161 MiB / 33.8% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
8558 (65 MiB / 29.3% CPU) |
8558 (65 MiB / 29.3% CPU) |
4938 (79 MiB / 39.8% CPU) |
5195 (79 MiB / 39.8% CPU) |
🥇 9023 (120 MiB / 28.7% CPU) |
🥇 9023 (120 MiB / 28.7% CPU) |
| HTTP/1 · TLS | HTTP/3 · QUIC |
2677 (112 MiB / 44.9% CPU) |
2677 (112 MiB / 44.9% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured | 🥇 4392 (81 MiB / 37.8% CPU) |
🥇 2176 (81 MiB / 37.8% CPU) |
3589 (152 MiB / 31.7% CPU) |
3589 (152 MiB / 31.7% CPU) |
| HTTP/2 · plain | HTTP/1 · plain |
12398 (88 MiB / 35.1% CPU) |
12398 (88 MiB / 35.1% CPU) |
10718 (58 MiB / 15.4% CPU) |
10718 (58 MiB / 15.4% CPU) |
7841 (54 MiB / 18% CPU) |
7841 (54 MiB / 18% CPU) |
3391 (74 MiB / 20.7% CPU) |
3391 (74 MiB / 20.7% CPU) |
🥇 12934 (105 MiB / 37.8% CPU) |
🥇 12934 (105 MiB / 37.8% CPU) |
| HTTP/2 · plain | HTTP/1 · TLS |
13814 (94 MiB / 38.6% CPU) |
13814 (94 MiB / 38.6% CPU) |
12186 (85 MiB / 16% CPU) |
12186 (85 MiB / 16% CPU) |
6688 (56 MiB / 18.9% CPU) |
6688 (56 MiB / 18.9% CPU) |
5045 (77 MiB / 21.3% CPU) |
5045 (77 MiB / 21.3% CPU) |
🥇 14337 (121 MiB / 42.3% CPU) |
🥇 14337 (121 MiB / 42.3% CPU) |
| HTTP/2 · plain | HTTP/2 · plain | 🥇 30992 (73 MiB / 25.8% CPU) |
🥇 30992 (73 MiB / 25.8% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
8615 (54 MiB / 17.5% CPU) |
8615 (54 MiB / 17.5% CPU) |
5442 (72 MiB / 20% CPU) |
5442 (72 MiB / 20% CPU) |
22898 (109 MiB / 37.5% CPU) |
22898 (109 MiB / 37.5% CPU) |
| HTTP/2 · plain | HTTP/2 · TLS | 🥇 24573 (77 MiB / 27.7% CPU) |
🥇 24573 (77 MiB / 27.7% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
11876 (58 MiB / 19% CPU) |
11876 (58 MiB / 19% CPU) |
6393 (73 MiB / 20.2% CPU) |
6393 (73 MiB / 20.2% CPU) |
18700 (116 MiB / 34.6% CPU) |
18700 (116 MiB / 34.6% CPU) |
| HTTP/2 · plain | HTTP/3 · QUIC |
4840 (97 MiB / 49.3% CPU) |
4840 (97 MiB / 49.3% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
3760 (74 MiB / 22.4% CPU) |
3760 (74 MiB / 22.4% CPU) |
🥇 8092 (117 MiB / 33.9% CPU) |
🥇 8092 (117 MiB / 33.9% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain | 🥇 13829 (94 MiB / 39.2% CPU) |
🥇 13829 (94 MiB / 39.2% CPU) |
9070 (73 MiB / 14.5% CPU) |
9070 (73 MiB / 14.5% CPU) |
5237 (66 MiB / 16.1% CPU) |
5237 (66 MiB / 16.1% CPU) |
3516 (81 MiB / 20.4% CPU) |
3516 (81 MiB / 20.4% CPU) |
13572 (113 MiB / 37.3% CPU) |
13572 (113 MiB / 37.3% CPU) |
| HTTP/2 · TLS | HTTP/1 · TLS | 🥇 9182 (96 MiB / 37.3% CPU) |
🥇 9182 (96 MiB / 37.3% CPU) |
6812 (90 MiB / 15.3% CPU) |
6812 (90 MiB / 15.3% CPU) |
4920 (66 MiB / 18.2% CPU) |
4920 (66 MiB / 18.2% CPU) |
2505 (83 MiB / 20.1% CPU) |
2505 (83 MiB / 20.1% CPU) |
9080 (124 MiB / 40.6% CPU) |
9080 (124 MiB / 40.6% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain | 🥇 47325 (81 MiB / 29% CPU) |
🥇 47325 (81 MiB / 29% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
5750 (67 MiB / 5.9% CPU) |
2851 (67 MiB / 5.9% CPU) |
4704 (79 MiB / 19% CPU) |
4704 (79 MiB / 19% CPU) |
22095 (115 MiB / 37.6% CPU) |
22095 (115 MiB / 37.6% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS | 🥇 29038 (83 MiB / 27.1% CPU) |
🥇 29038 (83 MiB / 27.1% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
6022 (67 MiB / 18.3% CPU) |
6022 (67 MiB / 18.3% CPU) |
4494 (79 MiB / 18.6% CPU) |
4494 (79 MiB / 18.6% CPU) |
16458 (118 MiB / 35% CPU) |
16458 (118 MiB / 35% CPU) |
| HTTP/2 · TLS | HTTP/3 · QUIC |
4226 (108 MiB / 36% CPU) |
4226 (108 MiB / 36% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured |
1772 (81 MiB / 21.6% CPU) |
1772 (81 MiB / 21.6% CPU) |
🥇 4805 (125 MiB / 33.7% CPU) |
🥇 4805 (125 MiB / 33.7% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain |
5834 (101 MiB / 39.5% CPU) |
5834 (101 MiB / 39.5% CPU) |
0 (63 MiB / 11.2% CPU) |
7464 (63 MiB / 11.2% CPU) |
🥇 10727 (66 MiB / 22% CPU) |
🥇 10727 (66 MiB / 22% CPU) |
1595 (85 MiB / 26.4% CPU) |
1595 (85 MiB / 26.4% CPU) |
5558 (187 MiB / 35% CPU) |
5558 (187 MiB / 35% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS |
3723 (119 MiB / 38.2% CPU) |
3723 (119 MiB / 38.2% CPU) |
0 (66 MiB / 11.1% CPU) |
4316 (66 MiB / 11.1% CPU) |
🥇 5430 (69 MiB / 21.8% CPU) |
🥇 5430 (69 MiB / 21.8% CPU) |
Not measured | Not measured |
4786 (197 MiB / 35.5% CPU) |
4786 (197 MiB / 35.5% CPU) |
| HTTP/3 · QUIC | HTTP/2 · plain |
4576 (97 MiB / 41.3% CPU) |
4576 (97 MiB / 41.3% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) | 🥇 5954 (66 MiB / 14.4% CPU) |
🥇 5954 (66 MiB / 14.4% CPU) |
Not measured | Not measured |
5642 (176 MiB / 34.2% CPU) |
5642 (176 MiB / 34.2% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS |
5532 (106 MiB / 41.4% CPU) |
5532 (106 MiB / 41.4% CPU) |
Not possible (no H2 upstream) | Not possible (no H2 upstream) |
6633 (68 MiB / 21.5% CPU) |
6633 (68 MiB / 21.5% CPU) |
Not measured | Not measured | 🥇 7854 (167 MiB / 32.5% CPU) |
🥇 7854 (167 MiB / 32.5% CPU) |
| HTTP/3 · QUIC | HTTP/3 · QUIC |
3460 (96 MiB / 35% CPU) |
3460 (96 MiB / 35% CPU) |
Not possible (no H3 upstream) | Not possible (no H3 upstream) | Not measured | Not measured | Not measured | Not measured | 🥇 4659 (188 MiB / 34.4% CPU) |
🥇 4659 (188 MiB / 34.4% CPU) |
Same Client×Origin wires with interception on (compare-product 34441526151). 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.50× and Full ≥ 0.50× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.70× (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. @ 9a2b3a1e: 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 |
11715 (84 MiB / 35.6% CPU) |
11001 (84 MiB / 31.8% CPU) |
0.97× | 0.91× |
| HTTP/1 · plain | HTTP/1 · TLS |
7193 (94 MiB / 31.1% CPU) |
8678 (94 MiB / 35.3% CPU) |
0.65× | 0.78× |
| HTTP/1 · plain | HTTP/2 · plain |
14866 (89 MiB / 36.3% CPU) |
13716 (90 MiB / 35.7% CPU) |
0.77× | 0.71× |
| HTTP/1 · plain | HTTP/2 · TLS |
11164 (109 MiB / 33.4% CPU) |
9186 (108 MiB / 32.6% CPU) |
1.12× | 0.93× |
| HTTP/1 · plain | HTTP/3 · QUIC |
3459 (91 MiB / 39.6% CPU) |
3710 (90 MiB / 40.3% CPU) |
0.94× | 1.01× |
| HTTP/1 · TLS | HTTP/1 · plain |
8052 (95 MiB / 30.3% CPU) |
10123 (96 MiB / 33.1% CPU) |
0.57× | 0.71× |
| HTTP/1 · TLS | HTTP/1 · TLS |
6914 (98 MiB / 36% CPU) |
9190 (98 MiB / 33.3% CPU) |
1.13× | 1.5× |
| HTTP/1 · TLS | HTTP/2 · plain |
8443 (123 MiB / 35.7% CPU) |
8055 (102 MiB / 33.1% CPU) |
1.02× | 0.97× |
| HTTP/1 · TLS | HTTP/2 · TLS |
8738 (133 MiB / 33.2% CPU) |
7260 (157 MiB / 32.6% CPU) |
1.03× | 0.86× |
| HTTP/1 · TLS | HTTP/3 · QUIC |
2633 (105 MiB / 42.6% CPU) |
2842 (102 MiB / 46.3% CPU) |
0.98× | 1.06× |
| HTTP/2 · plain | HTTP/1 · plain |
13742 (88 MiB / 38.4% CPU) |
13895 (90 MiB / 37.5% CPU) |
1.11× | 1.12× |
| HTTP/2 · plain | HTTP/1 · TLS |
14725 (96 MiB / 40.8% CPU) |
10701 (96 MiB / 35.5% CPU) |
1.07× | 0.77× |
| HTTP/2 · plain | HTTP/2 · plain |
24126 (76 MiB / 29.9% CPU) |
24457 (76 MiB / 29.7% CPU) |
0.78× | 0.79× |
| HTTP/2 · plain | HTTP/2 · TLS |
23664 (83 MiB / 31.7% CPU) |
24893 (81 MiB / 31% CPU) |
0.96× | 1.01× |
| HTTP/2 · plain | HTTP/3 · QUIC |
4822 (96 MiB / 45% CPU) |
5035 (95 MiB / 45.9% CPU) |
1× | 1.04× |
| HTTP/2 · TLS | HTTP/1 · plain |
13114 (92 MiB / 38.3% CPU) |
12954 (91 MiB / 38.1% CPU) |
0.95× | 0.94× |
| HTTP/2 · TLS | HTTP/1 · TLS |
9205 (97 MiB / 36% CPU) |
9788 (97 MiB / 39.1% CPU) |
1× | 1.07× |
| HTTP/2 · TLS | HTTP/2 · plain |
28081 (86 MiB / 32% CPU) |
24916 (87 MiB / 30.4% CPU) |
0.59× | 0.53× |
| HTTP/2 · TLS | HTTP/2 · TLS |
22874 (87 MiB / 29.6% CPU) |
29489 (89 MiB / 32% CPU) |
0.79× | 1.02× |
| HTTP/2 · TLS | HTTP/3 · QUIC |
4679 (105 MiB / 37.9% CPU) |
4453 (106 MiB / 39.3% CPU) |
1.11× | 1.05× |
| HTTP/3 · QUIC | HTTP/1 · plain |
4000 (102 MiB / 35% CPU) |
4530 (102 MiB / 38.8% CPU) |
0.69× | 0.78× |
| HTTP/3 · QUIC | HTTP/1 · TLS |
3931 (116 MiB / 38.3% CPU) |
3992 (115 MiB / 40% CPU) |
1.06× | 1.07× |
| HTTP/3 · QUIC | HTTP/2 · plain |
4272 (98 MiB / 39% CPU) |
4718 (98 MiB / 39.8% CPU) |
0.93× | 1.03× |
| HTTP/3 · QUIC | HTTP/2 · TLS |
5183 (106 MiB / 38.5% CPU) |
4838 (108 MiB / 38.9% CPU) |
0.94× | 0.87× |
| HTTP/3 · QUIC | HTTP/3 · QUIC |
3211 (94 MiB / 37.3% CPU) |
3887 (95 MiB / 37.3% CPU) |
0.93× | 1.12× |
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 @ 6d2a7c9d. Source: Actions 33259699099. Warmup 2s / measure 8s; concurrency 8–64; sustain = median peak RPS among SLO-pass steps @ c=64. RPS cells include (MiB / CPU%) at that step.
| Arm | Win sustain | Win peak | Linux sustain | Linux peak | Win÷ | Lin÷ | Gate |
|---|---|---|---|---|---|---|---|
twp-cli-reverse-http1 vs library |
32214 (118 MiB / 43.4% CPU) |
32219 |
47734 (151 MiB / 49.0% CPU) |
47844 | 1.02× | 1.04× | ≥ 0.80× |
twp-cli-reverse-http1-tls vs library TLS |
26495 (138 MiB / 48.3% CPU) |
26651 |
36932 (174 MiB / 48.5% CPU) |
37200 | 1.01× | 1.00× | ≥ 0.80× |
twp-cli-reverse-http1-route vs CLI |
32394 (120 MiB / 47.7% CPU) |
32428 |
47369 (149 MiB / 49.2% CPU) |
47398 | 1.01× | 0.99× | ≥ 0.90× |
twp-cli-plus-base-http1 vs CLI |
32475 (123 MiB / 46.0% CPU) |
32695 |
47322 (154 MiB / 49.8% CPU) |
47744 | 1.01× | 0.99× | ≥ 0.90× |
twp-cli-plus-cache-http1 (cold) vs CLI |
34029 (118 MiB / 64.5% CPU) |
34460 |
49409 (148 MiB / 62.3% CPU) |
50104 | 1.06× | 1.04× | ≥ 0.70× |
twp-cli-intercept-http1 vs CLI |
25219 (124 MiB / 54.0% CPU) |
25377 |
35969 (155 MiB / 51.2% CPU) |
35977 | 0.78× | 0.75× | ≥ 0.70× |
twp-cli-plus-waf-http1 vs CLI |
31653 (124 MiB / 46.8% CPU) |
31837 |
46367 (151 MiB / 49.6% CPU) |
46685 | 0.98× | 0.97× | ≥ 0.80× |
twp-cli-plus-cidr-http1 vs CLI |
31730 (123 MiB / 51.1% CPU) |
31757 |
46828 (150 MiB / 50.2% CPU) |
47093 | 0.98× | 0.98× | ≥ 0.80× |
twp-cli-plus-jwt-http1 vs CLI |
30326 (138 MiB / 46.6% CPU) |
30386 |
43906 (179 MiB / 49.5% CPU) |
44433 | 0.94× | 0.92× | ≥ 0.70× |
twp-cli-plus-ratelimit-http1 vs CLI |
31713 (123 MiB / 48.8% CPU) |
31944 |
46435 (151 MiB / 49.4% CPU) |
46860 | 0.98× | 0.97× | ≥ 0.80× |
twp-cli-plus-resilience-http1 vs CLI |
32373 (128 MiB / 50.5% CPU) |
32416 |
47302 (155 MiB / 49.5% CPU) |
47654 | 1.00× | 0.99× | ≥ 0.85× |
twp-cli-plus-discovery-file-http1 vs CLI |
32209 (121 MiB / 46.5% CPU) |
32332 |
47485 (151 MiB / 49.1% CPU) |
47770 | 1.00× | 0.99× | ≥ 0.80× |
twp-cli-plus-metrics-scrape-http1 vs CLI |
32192 (126 MiB / 44.8% CPU) |
32427 |
47348 (159 MiB / 49.5% CPU) |
48665 | 1.00× | 0.99× | ≥ 0.80× |
twp-cli-plus-cache-hit-http1 vs cache cold |
34015 (117 MiB / 66.0% CPU) |
34022 |
49153 (149 MiB / 63.1% CPU) |
49358 | 1.00× | 0.99× | ≥ 0.90× |
twp-cli-static-http1 vs CLI |
53078 (109 MiB / 52.1% CPU) |
53980 |
78333 (150 MiB / 48.4% CPU) |
82955 | 1.65× | 1.64× | ≥ 0.85× |
twp-cli-logging-http1 vs CLI |
32440 (119 MiB / 47.4% CPU) |
32581 |
47624 (150 MiB / 49.3% CPU) |
48604 | 1.01× | 1.00× | ≥ 0.90× |
twp-cli-lb-leasttime-http1 vs route |
29649 (134 MiB / 54.7% CPU) |
30386 |
44790 (180 MiB / 50.6% CPU) |
44825 | 0.92× | 0.95× | ≥ 0.85× |
twp-cli-dialect-twp-http1 vs CLI |
32461 (114 MiB / 49.7% CPU) |
32780 |
47898 (145 MiB / 49.3% CPU) |
48154 | 1.01× | 1.00× | ≥ 0.90× |
validate-edition-gates.ps1 passed on both OS for this run. Library baselines @ c=64 (same job): Win H1 31561 / TLS 26128; Linux H1 45818 / TLS 36869. Laptop smoke ratios stay on Performance Local Lab — Editions.
Same reverse matrix measured on Titanium 7.0 versus committed 6.0 baselines (33087088466). Median of 3 @ 0ef6d4dd. Source: Actions 33270571908. Sustain RPS @ c=64. Absolute 7.0÷6.0 ≈ 0.96–1.00× on same-protocol arms; peer-normalized ratios ≈ 0.90–1.03×.
| Arm | Win 6.0 | Win 7.0 | Win÷ | Linux 6.0 | Linux 7.0 | Lin÷ |
|---|---|---|---|---|---|---|
twp-reverse-http1 |
32525 | 32213 | 0.99× | 32755 | 31932 | 0.97× |
twp-reverse-http1-tls |
26461 | 26570 | 1.00× | 22784 | 22351 | 0.98× |
twp-reverse-http2 |
76038 | 75316 | 0.99× | 47690 | 46678 | 0.98× |
twp-reverse-http2-cleartext |
40347 | 40236 | 1.00× | 34556 | 33316 | 0.96× |
twp-reverse-http3 |
17529 | 17380 | 0.99× | 19468 | 19423 | 1.00× |
twp-reverse-http3-cleartext |
19956 | 19529 | 0.98× | 20395 | 20070 | 0.98× |
yarp-reverse-http1 (peer) |
27504 | 27254 | 0.99× | 27713 | 27102 | 0.98× |
yarp-reverse-http2 (peer) |
35553 | 35189 | 0.99× | 29111 | 28733 | 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.
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). Charts: rps-heavier-*-{windows,linux}.png. 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 — Titanium is behind YARP here (~0.69–0.76× at 64 KiB), 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 gets per-datagram delay + drops (QUIC). Lossy tables publish HTTP/1, HTTP/2, and HTTP/3.

Median of 3 repeats on windows-latest @ 9a2b3a1e. Source: Actions 34441570199 (compare-bodies). Warmup 2s / measure 8s. RPS cells include (MiB / CPU%) footprints.
| Body | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 64 KiB | HTTP/1 · TLS | HTTP/1 · plain | 🥇 13,191 (122 MiB / 46.7% CPU) |
13,191 (122 MiB / 46.7% CPU) |
924 (142 MiB / 24.8% CPU) |
924 (142 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
11,473 (133 MiB / 47.5% CPU) |
11,473 (133 MiB / 47.5% CPU) |
| 64 KiB | HTTP/2 · TLS | HTTP/1 · plain | 🥇 11,769 (173 MiB / 46.2% CPU) |
11,769 (173 MiB / 46.2% CPU) |
898 (142 MiB / 24.8% CPU) |
898 (142 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
9,560 (135 MiB / 48.1% CPU) |
9,560 (135 MiB / 48.1% CPU) |
| 64 KiB | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 5,958 (139 MiB / 41.5% CPU) |
5,958 (139 MiB / 41.5% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
5,060 (185 MiB / 51.0% CPU) |
5,060 (185 MiB / 51.0% CPU) |
| 256 KiB | HTTP/1 · TLS | HTTP/1 · plain | 🥇 3,693 (136 MiB / 42.1% CPU) |
3,693 (136 MiB / 42.1% CPU) |
241 (142 MiB / 24.9% CPU) |
241 (142 MiB / 24.9% CPU) |
Not possible | Not possible | Not possible | Not possible |
3,325 (130 MiB / 47.2% CPU) |
3,325 (130 MiB / 47.2% CPU) |
| 256 KiB | HTTP/2 · TLS | HTTP/1 · plain | 🥇 3,784 (149 MiB / 36.2% CPU) |
3,784 (149 MiB / 36.2% CPU) |
230 (142 MiB / 24.8% CPU) |
230 (142 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
2,625 (135 MiB / 45.2% CPU) |
2,625 (135 MiB / 45.2% CPU) |
| 256 KiB | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 1,535 (111 MiB / 40.4% CPU) |
1,535 (111 MiB / 40.4% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
1,385 (169 MiB / 44.6% CPU) |
1,385 (169 MiB / 44.6% CPU) |
| 64 KiB | HTTP/2 · plain | HTTP/1 · plain | 🥇 14,010 (174 MiB / 40.5% CPU) |
14,010 (174 MiB / 40.5% CPU) |
5,865 (127 MiB / 24.0% CPU) |
5,865 (127 MiB / 24.0% CPU) |
Not possible | Not possible | Not possible | Not possible |
12,443 (111 MiB / 39.6% CPU) |
12,443 (111 MiB / 39.6% CPU) |
| 64 KiB | HTTP/2 · TLS | HTTP/2 · plain |
6,319 (79 MiB / 38.1% CPU) |
6,319 (79 MiB / 38.1% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 9,493 (131 MiB / 50.3% CPU) |
9,493 (131 MiB / 50.3% CPU) |
| 64 KiB | HTTP/2 · TLS | HTTP/2 · TLS |
5,660 (80 MiB / 36.4% CPU) |
5,660 (80 MiB / 36.4% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 8,201 (141 MiB / 47.6% CPU) |
8,201 (141 MiB / 47.6% CPU) |
| 64 KiB | HTTP/3 · QUIC | HTTP/2 · TLS |
4,657 (129 MiB / 46.1% CPU) |
4,657 (129 MiB / 46.1% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 5,021 (195 MiB / 47.6% CPU) |
5,021 (195 MiB / 47.6% CPU) |
| 64 KiB | HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 6,149 (148 MiB / 42.2% CPU) |
6,149 (148 MiB / 42.2% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
4,496 (191 MiB / 49.0% CPU) |
4,496 (191 MiB / 49.0% CPU) |
| 256 KiB | HTTP/2 · plain | HTTP/1 · plain | 🥇 4,457 (144 MiB / 31.8% CPU) |
4,457 (144 MiB / 31.8% CPU) |
1,759 (127 MiB / 23.6% CPU) |
1,759 (127 MiB / 23.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
3,719 (123 MiB / 37.8% CPU) |
3,719 (123 MiB / 37.8% CPU) |
| 256 KiB | HTTP/2 · TLS | HTTP/2 · plain |
1,976 (84 MiB / 29.9% CPU) |
1,976 (84 MiB / 29.9% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 2,325 (169 MiB / 46.5% CPU) |
2,325 (169 MiB / 46.5% CPU) |
| 256 KiB | HTTP/2 · TLS | HTTP/2 · TLS |
1,758 (87 MiB / 29.7% CPU) |
1,758 (87 MiB / 29.7% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 2,109 (153 MiB / 44.9% CPU) |
2,109 (153 MiB / 44.9% CPU) |
| 256 KiB | HTTP/3 · QUIC | HTTP/2 · TLS |
1,164 (153 MiB / 43.0% CPU) |
1,164 (153 MiB / 43.0% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 1,319 (186 MiB / 44.8% CPU) |
1,319 (186 MiB / 44.8% CPU) |
| 256 KiB | HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 1,404 (146 MiB / 41.0% CPU) |
1,404 (146 MiB / 41.0% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
1,269 (196 MiB / 44.8% CPU) |
1,269 (196 MiB / 44.8% CPU) |
nginx/Windows collapses on large reverse bodies in this harness; treat as same-OS only. H1 TLS 64 KiB ≈ 1.11× YARP; 256 KiB ≈ 1.23×. H2→H1 64 KiB ≈ 1.21×; H3→H1 64 KiB ≈ 1.18×.

Median of 3 repeats @ 9a2b3a1e. Source: Actions 34441570199 (compare-bodies). Warmup 2s / measure 8s.
| Body | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 64 KiB | HTTP/1 · TLS | HTTP/1 · plain |
8,010 (176 MiB / 45.3% CPU) |
8,010 (176 MiB / 45.3% CPU) |
5,555 (100 MiB / 52.4% CPU) |
5,555 (100 MiB / 52.4% CPU) |
🥇 9,012 (84 MiB / 40.6% CPU) |
9,012 (84 MiB / 40.6% CPU) |
7,524 (131 MiB / 45.8% CPU) |
7,524 (131 MiB / 45.8% CPU) |
6,504 (169 MiB / 48.4% CPU) |
6,504 (169 MiB / 48.4% CPU) |
| 64 KiB | HTTP/2 · TLS | HTTP/1 · plain |
9,850 (231 MiB / 39.4% CPU) |
9,850 (231 MiB / 39.4% CPU) |
3,314 (103 MiB / 10.9% CPU) |
3,314 (103 MiB / 10.9% CPU) |
🥇 10,145 (87 MiB / 24.0% CPU) |
10,145 (87 MiB / 24.0% CPU) |
8,389 (141 MiB / 23.7% CPU) |
8,389 (141 MiB / 23.7% CPU) |
8,231 (164 MiB / 46.6% CPU) |
8,231 (164 MiB / 46.6% CPU) |
| 64 KiB | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 5,646 (184 MiB / 44.2% CPU) |
5,646 (184 MiB / 44.2% CPU) |
0 (129 MiB / 22.4% CPU) |
1,678 (129 MiB / 22.4% CPU) |
Not measured | Not measured | Not measured | Not measured |
4,421 (231 MiB / 51.5% CPU) |
4,421 (231 MiB / 51.5% CPU) |
| 256 KiB | HTTP/1 · TLS | HTTP/1 · plain |
2,766 (128 MiB / 37.7% CPU) |
2,766 (128 MiB / 37.7% CPU) |
1,735 (100 MiB / 53.6% CPU) |
1,735 (100 MiB / 53.6% CPU) |
🥇 2,974 (83 MiB / 34.0% CPU) |
2,974 (83 MiB / 34.0% CPU) |
2,701 (145 MiB / 33.9% CPU) |
2,701 (145 MiB / 33.9% CPU) |
2,164 (166 MiB / 45.8% CPU) |
2,164 (166 MiB / 45.8% CPU) |
| 256 KiB | HTTP/2 · TLS | HTTP/1 · plain |
2,550 (209 MiB / 32.3% CPU) |
2,550 (209 MiB / 32.3% CPU) |
1,728 (103 MiB / 19.0% CPU) |
1,728 (103 MiB / 19.0% CPU) |
🥇 3,276 (86 MiB / 19.5% CPU) |
3,276 (86 MiB / 19.5% CPU) |
3,128 (168 MiB / 20.2% CPU) |
3,128 (168 MiB / 20.2% CPU) |
2,201 (163 MiB / 43.1% CPU) |
2,201 (163 MiB / 43.1% CPU) |
| 256 KiB | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 1,458 (159 MiB / 43.3% CPU) |
1,458 (159 MiB / 43.3% CPU) |
0 (120 MiB / 20.0% CPU) |
24 (120 MiB / 20.0% CPU) |
Not measured | Not measured | Not measured | Not measured |
1,278 (218 MiB / 48.1% CPU) |
1,278 (218 MiB / 48.1% CPU) |
| 64 KiB | HTTP/2 · plain | HTTP/1 · plain | 🥇 13,722 (251 MiB / 41.5% CPU) |
13,722 (251 MiB / 41.5% CPU) |
4,929 (80 MiB / 10.4% CPU) |
4,929 (80 MiB / 10.4% CPU) |
13,114 (72 MiB / 23.9% CPU) |
13,114 (72 MiB / 23.9% CPU) |
11,084 (133 MiB / 23.5% CPU) |
11,084 (133 MiB / 23.5% CPU) |
13,042 (143 MiB / 45.4% CPU) |
13,042 (143 MiB / 45.4% CPU) |
| 64 KiB | HTTP/2 · TLS | HTTP/2 · plain |
3,419 (101 MiB / 40.6% CPU) |
3,419 (101 MiB / 40.6% CPU) |
Not possible | Not possible |
0 (83 MiB / 19.9% CPU) |
0 (83 MiB / 19.9% CPU) |
0 (126 MiB / 22.4% CPU) |
0 (126 MiB / 22.4% CPU) |
🥇 4,578 (185 MiB / 46.3% CPU) |
4,578 (185 MiB / 46.3% CPU) |
| 64 KiB | HTTP/2 · TLS | HTTP/2 · TLS |
4,682 (104 MiB / 37.3% CPU) |
4,682 (104 MiB / 37.3% CPU) |
Not possible | Not possible |
3,969 (83 MiB / 24.5% CPU) |
3,969 (83 MiB / 24.5% CPU) |
🥇 6,266 (154 MiB / 23.0% CPU) |
6,266 (154 MiB / 23.0% CPU) |
6,174 (184 MiB / 46.0% CPU) |
6,174 (184 MiB / 46.0% CPU) |
| 64 KiB | HTTP/3 · QUIC | HTTP/2 · TLS |
2,941 (168 MiB / 52.7% CPU) |
2,941 (168 MiB / 52.7% CPU) |
Not possible | Not possible | Not measured | Not measured | Not measured | Not measured | 🥇 3,040 (234 MiB / 48.9% CPU) |
3,040 (234 MiB / 48.9% CPU) |
| 64 KiB | HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 6,442 (214 MiB / 41.6% CPU) |
6,442 (214 MiB / 41.6% CPU) |
0 (145 MiB / 17.0% CPU) |
2,278 (145 MiB / 17.0% CPU) |
Not measured | Not measured | Not measured | Not measured |
4,761 (248 MiB / 49.0% CPU) |
4,761 (248 MiB / 49.0% CPU) |
| 256 KiB | HTTP/2 · plain | HTTP/1 · plain |
3,918 (201 MiB / 33.0% CPU) |
3,918 (201 MiB / 33.0% CPU) |
1,946 (80 MiB / 14.2% CPU) |
1,946 (80 MiB / 14.2% CPU) |
4,300 (72 MiB / 18.6% CPU) |
4,300 (72 MiB / 18.6% CPU) |
🥇 4,479 (153 MiB / 21.0% CPU) |
4,479 (153 MiB / 21.0% CPU) |
3,945 (157 MiB / 38.1% CPU) |
3,945 (157 MiB / 38.1% CPU) |
| 256 KiB | HTTP/2 · TLS | HTTP/2 · plain |
1,139 (111 MiB / 33.5% CPU) |
1,139 (111 MiB / 33.5% CPU) |
Not possible | Not possible |
0 (81 MiB / 19.8% CPU) |
0 (81 MiB / 19.8% CPU) |
0 (126 MiB / 22.3% CPU) |
0 (126 MiB / 22.3% CPU) |
🥇 1,293 (186 MiB / 42.8% CPU) |
1,293 (186 MiB / 42.8% CPU) |
| 256 KiB | HTTP/2 · TLS | HTTP/2 · TLS |
1,436 (114 MiB / 28.9% CPU) |
1,436 (114 MiB / 28.9% CPU) |
Not possible | Not possible |
1,114 (89 MiB / 24.3% CPU) |
1,114 (89 MiB / 24.3% CPU) |
🥇 1,826 (160 MiB / 22.8% CPU) |
1,826 (160 MiB / 22.8% CPU) |
1,734 (185 MiB / 42.8% CPU) |
1,734 (185 MiB / 42.8% CPU) |
| 256 KiB | HTTP/3 · QUIC | HTTP/2 · TLS |
837 (187 MiB / 51.5% CPU) |
837 (187 MiB / 51.5% CPU) |
Not possible | Not possible | Not measured | Not measured | Not measured | Not measured | 🥇 915 (246 MiB / 46.9% CPU) |
915 (246 MiB / 46.9% CPU) |
| 256 KiB | HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 1,745 (189 MiB / 42.2% CPU) |
1,745 (189 MiB / 42.2% CPU) |
0 (143 MiB / 18.7% CPU) |
619 (143 MiB / 18.7% CPU) |
Not measured | Not measured | Not measured | Not measured |
1,487 (249 MiB / 47.5% CPU) |
1,487 (249 MiB / 47.5% CPU) |
On this GHA pass TWP÷YARP H1 TLS ≈ 1.23× (64 KiB) / 1.28× (256 KiB); H2→H1 ≈ 1.23× / 1.16×; H3→H1 ≈ 1.27× / 1.13×. TWP÷nginx H1 TLS ≈ 1.43 / 1.58. Absolute RPS swings by VM; prefer ratios.

Median of 3 repeats on windows-latest @ 9a2b3a1e. Source: Actions 34441591377 (compare-post).
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain | 🥇 5,883 (94 MiB / 46.6% CPU) |
5,883 (94 MiB / 46.6% CPU) |
352 (142 MiB / 24.7% CPU) |
352 (142 MiB / 24.7% CPU) |
Not possible | Not possible | Not possible | Not possible |
4,093 (137 MiB / 55.7% CPU) |
4,093 (137 MiB / 55.7% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain | 🥇 3,997 (184 MiB / 49.3% CPU) |
3,997 (184 MiB / 49.3% CPU) |
352 (143 MiB / 24.7% CPU) |
352 (143 MiB / 24.7% CPU) |
Not possible | Not possible | Not possible | Not possible |
3,494 (134 MiB / 52.1% CPU) |
3,494 (134 MiB / 52.1% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain | 🥇 2,039 (162 MiB / 40.4% CPU) |
2,039 (162 MiB / 40.4% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
1,893 (203 MiB / 48.6% CPU) |
1,893 (203 MiB / 48.6% CPU) |
| HTTP/2 · plain | HTTP/1 · plain | 🥇 6,237 (182 MiB / 46.1% CPU) |
6,237 (182 MiB / 46.1% CPU) |
1,622 (130 MiB / 24.6% CPU) |
1,622 (130 MiB / 24.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
5,889 (125 MiB / 53.5% CPU) |
5,889 (125 MiB / 53.5% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain |
8 (83 MiB / 0.2% CPU) |
8 (83 MiB / 0.2% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 3,602 (143 MiB / 49.3% CPU) |
3,602 (143 MiB / 49.3% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS |
8 (88 MiB / 0.1% CPU) |
8 (88 MiB / 0.1% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 2,837 (142 MiB / 46.8% CPU) |
2,837 (142 MiB / 46.8% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS |
1,769 (178 MiB / 44.3% CPU) |
1,769 (178 MiB / 44.3% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 1,857 (194 MiB / 47.4% CPU) |
1,857 (194 MiB / 47.4% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 1,935 (175 MiB / 42.5% CPU) |
1,935 (175 MiB / 42.5% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
1,768 (213 MiB / 47.8% CPU) |
1,768 (213 MiB / 47.8% CPU) |
TWP leads H1 POST (1.5× YARP), H2 POST (1.2× YARP), and H3 POST (~1.1× YARP).

Median of 3 repeats @ 9a2b3a1e. Source: Actions 34441591377 (compare-post).
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain |
5,113 (133 MiB / 43.7% CPU) |
5,113 (133 MiB / 43.7% CPU) |
4,029 (100 MiB / 48.2% CPU) |
4,029 (100 MiB / 48.2% CPU) |
5,273 (86 MiB / 40.5% CPU) |
5,273 (86 MiB / 40.5% CPU) |
🥇 5,337 (131 MiB / 40.4% CPU) |
5,337 (131 MiB / 40.4% CPU) |
3,410 (176 MiB / 54.5% CPU) |
3,410 (176 MiB / 54.5% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain | 🥇 3,356 (220 MiB / 47.1% CPU) |
3,356 (220 MiB / 47.1% CPU) |
1,563 (116 MiB / 21.2% CPU) |
1,563 (116 MiB / 21.2% CPU) |
2,012 (83 MiB / 24.0% CPU) |
2,012 (83 MiB / 24.0% CPU) |
0 (146 MiB / 20.5% CPU) |
2,914 (146 MiB / 20.5% CPU) |
2,767 (171 MiB / 47.9% CPU) |
2,767 (171 MiB / 47.9% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain | 🥇 3,052 (226 MiB / 43.7% CPU) |
3,052 (226 MiB / 43.7% CPU) |
557 (110 MiB / 25.0% CPU) |
557 (110 MiB / 25.0% CPU) |
Not measured | Not measured | Not measured | Not measured |
2,778 (240 MiB / 49.2% CPU) |
2,778 (240 MiB / 49.2% CPU) |
| HTTP/2 · plain | HTTP/1 · plain | 🥇 5,779 (234 MiB / 42.8% CPU) |
5,779 (234 MiB / 42.8% CPU) |
2,488 (94 MiB / 22.3% CPU) |
2,488 (94 MiB / 22.3% CPU) |
3,011 (70 MiB / 23.1% CPU) |
3,011 (70 MiB / 23.1% CPU) |
0 (134 MiB / 22.7% CPU) |
5,195 (134 MiB / 22.7% CPU) |
4,169 (161 MiB / 41.4% CPU) |
4,169 (161 MiB / 41.4% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain |
6 (118 MiB / 0.2% CPU) |
6 (118 MiB / 0.2% CPU) |
Not possible | Not possible |
0 (90 MiB / 23.7% CPU) |
0 (90 MiB / 23.7% CPU) |
0 (153 MiB / 22.1% CPU) |
0 (153 MiB / 22.1% CPU) |
🥇 2,620 (179 MiB / 46.6% CPU) |
2,620 (179 MiB / 46.6% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS |
8 (115 MiB / 0.3% CPU) |
8 (115 MiB / 0.3% CPU) |
Not possible | Not possible |
1,216 (88 MiB / 24.6% CPU) |
1,216 (88 MiB / 24.6% CPU) |
🥇 2,222 (147 MiB / 23.3% CPU) |
2,222 (147 MiB / 23.3% CPU) |
2,085 (181 MiB / 46.1% CPU) |
2,085 (181 MiB / 46.1% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS | 🥇 1,999 (236 MiB / 49.4% CPU) |
1,999 (236 MiB / 49.4% CPU) |
Not possible | Not possible | Not measured | Not measured | Not measured | Not measured |
1,950 (245 MiB / 46.9% CPU) |
1,950 (245 MiB / 46.9% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 2,301 (253 MiB / 44.2% CPU) |
2,301 (253 MiB / 44.2% CPU) |
471 (120 MiB / 25.1% CPU) |
471 (120 MiB / 25.1% CPU) |
Not measured | Not measured | Not measured | Not measured |
2,138 (274 MiB / 47.8% CPU) |
2,138 (274 MiB / 47.8% CPU) |
Linux nginx H1/H2/H3 POST completed (nginx.org mainline). TWP÷YARP H1 ≈ 1.5×; H2 ≈ 1.3×; H3 ≈ 1.1×. TWP÷nginx H3 ≈ 4×.

Userspace 5 ms one-way delay + 1% TCP connection stall (H1/H2) or UDP datagram drop (H3); 64 KiB GET. Median of 3 repeats on windows-latest @ 9a2b3a1e — 34441595456 (compare-lossy).
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain | 🥇 663 (112 MiB / 3.0% CPU) |
663 (112 MiB / 3.0% CPU) |
652 (143 MiB / 16.4% CPU) |
652 (143 MiB / 16.4% CPU) |
Not possible | Not possible | Not possible | Not possible |
662 (121 MiB / 4.5% CPU) |
662 (121 MiB / 4.5% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain |
0 (120 MiB / 1.4% CPU) |
86 (120 MiB / 1.4% CPU) |
0 (142 MiB / 0.6% CPU) |
17 (142 MiB / 0.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
0 (99 MiB / 0.8% CPU) |
16 (99 MiB / 0.8% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain |
0 (67 MiB / 0.1% CPU) |
0 (67 MiB / 0.1% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
0 (80 MiB / 0.0% CPU) |
0 (80 MiB / 0.0% CPU) |
| HTTP/2 · plain | HTTP/1 · plain |
0 (130 MiB / 1.5% CPU) |
89 (130 MiB / 1.5% CPU) |
0 (128 MiB / 0.1% CPU) |
17 (128 MiB / 0.1% CPU) |
Not possible | Not possible | Not possible | Not possible |
0 (91 MiB / 0.7% CPU) |
16 (91 MiB / 0.7% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain |
0 (68 MiB / 0.6% CPU) |
8 (68 MiB / 0.6% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible |
0 (100 MiB / 0.7% CPU) |
16 (100 MiB / 0.7% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS |
0 (71 MiB / 0.2% CPU) |
8 (71 MiB / 0.2% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible |
0 (99 MiB / 0.8% CPU) |
16 (99 MiB / 0.8% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS |
0 (70 MiB / 0.1% CPU) |
0 (70 MiB / 0.1% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible |
0 (80 MiB / 0.0% CPU) |
0 (80 MiB / 0.0% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS |
0 (69 MiB / 0.0% CPU) |
0 (69 MiB / 0.0% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
0 (78 MiB / 0.0% CPU) |
0 (78 MiB / 0.0% CPU) |
TWP H2 HOL leads (~3.31× YARP). H3 is the protocol-shape win vs H2 HOL on the same lossy session; Win H3 GHA remains 0 (laptop remeasure kept above).

Median of 3 repeats @ 9a2b3a1e. Source: 34441595456 (compare-lossy; lossy H3 uses quic-http3).
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain |
1,195 (142 MiB / 13.7% CPU) |
1,195 (142 MiB / 13.7% CPU) |
1,205 (100 MiB / 12.6% CPU) |
1,205 (100 MiB / 12.6% CPU) |
🥇 1,207 (83 MiB / 7.8% CPU) |
1,207 (83 MiB / 7.8% CPU) |
1,193 (128 MiB / 9.3% CPU) |
1,193 (128 MiB / 9.3% CPU) |
1,194 (150 MiB / 17.5% CPU) |
1,194 (150 MiB / 17.5% CPU) |
| HTTP/2 · TLS | HTTP/1 · plain | 🥇 314 (182 MiB / 7.0% CPU) |
314 (182 MiB / 7.0% CPU) |
40 (99 MiB / 0.3% CPU) |
40 (99 MiB / 0.3% CPU) |
40 (86 MiB / 0.3% CPU) |
40 (86 MiB / 0.3% CPU) |
40 (135 MiB / 0.4% CPU) |
40 (135 MiB / 0.4% CPU) |
40 (127 MiB / 1.5% CPU) |
40 (127 MiB / 1.5% CPU) |
| HTTP/3 · QUIC | HTTP/1 · plain |
319 (151 MiB / 13.9% CPU) |
319 (151 MiB / 13.9% CPU) |
92 (109 MiB / 2.8% CPU) |
92 (109 MiB / 2.8% CPU) |
Not measured | Not measured | Not measured | Not measured | 🥇 338 (177 MiB / 22.4% CPU) |
338 (177 MiB / 22.4% CPU) |
| HTTP/2 · plain | HTTP/1 · plain | 🥇 344 (174 MiB / 7.2% CPU) |
344 (174 MiB / 7.2% CPU) |
40 (78 MiB / 0.2% CPU) |
40 (78 MiB / 0.2% CPU) |
40 (69 MiB / 0.2% CPU) |
40 (69 MiB / 0.2% CPU) |
40 (129 MiB / 0.4% CPU) |
40 (129 MiB / 0.4% CPU) |
41 (121 MiB / 1.2% CPU) |
41 (121 MiB / 1.2% CPU) |
| HTTP/2 · TLS | HTTP/2 · plain |
0 (93 MiB / 0.6% CPU) |
13 (93 MiB / 0.6% CPU) |
Not possible | Not possible |
0 (83 MiB / 2.7% CPU) |
0 (83 MiB / 2.7% CPU) |
0 (127 MiB / 6.3% CPU) |
0 (127 MiB / 6.3% CPU) |
🥇 40 (129 MiB / 1.6% CPU) |
40 (129 MiB / 1.6% CPU) |
| HTTP/2 · TLS | HTTP/2 · TLS |
0 (95 MiB / 0.7% CPU) |
11 (95 MiB / 0.7% CPU) |
Not possible | Not possible | 🥇 42 (88 MiB / 0.6% CPU) |
42 (88 MiB / 0.6% CPU) |
40 (138 MiB / 0.5% CPU) |
40 (138 MiB / 0.5% CPU) |
40 (129 MiB / 1.6% CPU) |
40 (129 MiB / 1.6% CPU) |
| HTTP/3 · QUIC | HTTP/2 · TLS |
322 (140 MiB / 24.2% CPU) |
322 (140 MiB / 24.2% CPU) |
Not possible | Not possible | Not measured | Not measured | Not measured | Not measured | 🥇 333 (183 MiB / 24.3% CPU) |
333 (183 MiB / 24.3% CPU) |
| HTTP/3 · QUIC | HTTP/1 · TLS | 🥇 341 (161 MiB / 15.8% CPU) |
341 (161 MiB / 15.8% CPU) |
96 (113 MiB / 3.0% CPU) |
96 (113 MiB / 3.0% CPU) |
Not measured | Not measured | Not measured | Not measured |
340 (185 MiB / 23.1% CPU) |
340 (185 MiB / 23.1% CPU) |
TWP H2 HOL ≫ YARP (~7.7×). H3 TWP÷YARP ≈ 1×.


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 @ 9a2b3a1e (34441578556). 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 = echo round-trips/sec.
Lossy-link runs (slow network) are already published above; they are not a slow app reader.

| Scenario | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Slow consumer (256 KiB GET, throttled client read) | HTTP/1 · TLS | HTTP/1 · plain | 🥇 243 (101 MiB / 5.9% CPU) |
243 (101 MiB / 5.9% CPU) |
204 (143 MiB / 24.6% CPU) |
204 (143 MiB / 24.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
241 (109 MiB / 4.8% CPU) |
241 (109 MiB / 4.8% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/2 · TLS | HTTP/1 · plain |
256 (131 MiB / 3.7% CPU) |
256 (131 MiB / 3.7% CPU) |
230 (142 MiB / 24.4% CPU) |
230 (142 MiB / 24.4% CPU) |
Not possible | Not possible | Not possible | Not possible | 🥇 256 (111 MiB / 5.4% CPU) |
256 (111 MiB / 5.4% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 264 (101 MiB / 17.4% CPU) |
264 (101 MiB / 17.4% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
255 (158 MiB / 20.4% CPU) |
255 (158 MiB / 20.4% CPU) |
| Early response (origin writes after first request chunk) | HTTP/1 · TLS | HTTP/1 · plain | 🥇 5,611 (99 MiB / 45.5% CPU) |
5,611 (99 MiB / 45.5% CPU) |
363 (143 MiB / 24.7% CPU) |
363 (143 MiB / 24.7% CPU) |
Not possible | Not possible | Not possible | Not possible |
3,919 (138 MiB / 53.9% CPU) |
3,919 (138 MiB / 53.9% CPU) |
| Early response (origin writes after first request chunk) | HTTP/2 · TLS | HTTP/1 · plain | 🥇 3,934 (169 MiB / 48.8% CPU) |
3,934 (169 MiB / 48.8% CPU) |
0 (144 MiB / 24.8% CPU) |
318 (144 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
3,200 (135 MiB / 53.4% CPU) |
3,200 (135 MiB / 53.4% CPU) |
| Early response (origin writes after first request chunk) | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 3,012 (152 MiB / 42.0% CPU) |
3,012 (152 MiB / 42.0% CPU) |
Not possible (no QUIC) | Not possible (no QUIC) | Not possible | Not possible | Not possible | Not possible |
2,602 (203 MiB / 51.0% CPU) |
2,602 (203 MiB / 51.0% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/2 · plain | HTTP/1 · plain |
248 (112 MiB / 3.6% CPU) |
248 (112 MiB / 3.6% CPU) |
248 (127 MiB / 9.4% CPU) |
248 (127 MiB / 9.4% CPU) |
Not possible | Not possible | Not possible | Not possible | 🥇 256 (104 MiB / 6.0% CPU) |
256 (104 MiB / 6.0% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/2 · TLS | HTTP/2 · TLS |
0 (70 MiB / 1.0% CPU) |
8 (70 MiB / 1.0% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible | 🥇 256 (136 MiB / 9.3% CPU) |
256 (136 MiB / 9.3% CPU) |
| Early response (origin writes after first request chunk) | HTTP/2 · TLS | HTTP/2 · TLS |
0 (92 MiB / 0.1% CPU) |
0 (92 MiB / 0.1% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible |
0 (110 MiB / 0.1% CPU) |
0 (110 MiB / 0.1% CPU) |
| Duplex (both directions live) | HTTP/2 · TLS | HTTP/2 · TLS |
0 (91 MiB / 0.1% CPU) |
0 (91 MiB / 0.1% CPU) |
Not possible | Not possible | Not possible | Not possible | Not possible | Not possible |
0 (111 MiB / 0.1% CPU) |
0 (111 MiB / 0.1% CPU) |
| Duplex (WebSocket / extended CONNECT) | HTTP/1 · TLS | HTTP/1 · plain | 🥇 24,498 (97 MiB / 43.0% CPU) |
24,498 (97 MiB / 43.0% CPU) |
12,337 (143 MiB / 24.6% CPU) |
12,337 (143 MiB / 24.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
23,100 (89 MiB / 44.6% CPU) |
23,100 (89 MiB / 44.6% CPU) |

| Scenario | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Slow consumer (256 KiB GET, throttled client read) | HTTP/1 · TLS | HTTP/1 · plain |
469 (122 MiB / 9.7% CPU) |
469 (122 MiB / 9.7% CPU) |
411 (100 MiB / 9.9% CPU) |
411 (100 MiB / 9.9% CPU) |
466 (83 MiB / 6.0% CPU) |
466 (83 MiB / 6.0% CPU) |
🥇 470 (140 MiB / 6.2% CPU) |
470 (140 MiB / 6.2% CPU) |
411 (146 MiB / 14.2% CPU) |
411 (146 MiB / 14.2% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/2 · TLS | HTTP/1 · plain | 🥇 480 (149 MiB / 13.1% CPU) |
480 (149 MiB / 13.1% CPU) |
374 (102 MiB / 5.0% CPU) |
374 (102 MiB / 5.0% CPU) |
475 (85 MiB / 4.3% CPU) |
475 (85 MiB / 4.3% CPU) |
477 (149 MiB / 4.5% CPU) |
477 (149 MiB / 4.5% CPU) |
477 (146 MiB / 15.0% CPU) |
477 (146 MiB / 15.0% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 473 (131 MiB / 34.1% CPU) |
473 (131 MiB / 34.1% CPU) |
0 (119 MiB / 21.9% CPU) |
122 (119 MiB / 21.9% CPU) |
Not measured | Not measured | Not measured | Not measured |
468 (198 MiB / 39.8% CPU) |
468 (198 MiB / 39.8% CPU) |
| Early response (origin writes after first request chunk) | HTTP/1 · TLS | HTTP/1 · plain |
4,671 (132 MiB / 47.4% CPU) |
4,671 (132 MiB / 47.4% CPU) |
0 (100 MiB / 49.6% CPU) |
3,397 (100 MiB / 49.6% CPU) |
🥇 4,886 (84 MiB / 43.3% CPU) |
4,886 (84 MiB / 43.3% CPU) |
0 (129 MiB / 25.6% CPU) |
2,584 (129 MiB / 25.6% CPU) |
3,159 (176 MiB / 56.3% CPU) |
3,159 (176 MiB / 56.3% CPU) |
| Early response (origin writes after first request chunk) | HTTP/2 · TLS | HTTP/1 · plain | 🥇 3,266 (242 MiB / 48.3% CPU) |
3,266 (242 MiB / 48.3% CPU) |
0 (113 MiB / 24.7% CPU) |
1,365 (113 MiB / 24.7% CPU) |
2,480 (85 MiB / 24.6% CPU) |
2,480 (85 MiB / 24.6% CPU) |
0 (148 MiB / 23.9% CPU) |
2,653 (148 MiB / 23.9% CPU) |
2,198 (169 MiB / 48.3% CPU) |
2,198 (169 MiB / 48.3% CPU) |
| Early response (origin writes after first request chunk) | HTTP/3 · QUIC | HTTP/1 · plain | 🥇 4,504 (218 MiB / 43.2% CPU) |
4,504 (218 MiB / 43.2% CPU) |
0 (125 MiB / 22.7% CPU) |
1,090 (125 MiB / 22.7% CPU) |
Not measured | Not measured | Not measured | Not measured |
3,217 (274 MiB / 48.7% CPU) |
3,217 (274 MiB / 48.7% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/2 · plain | HTTP/1 · plain | 🥇 478 (136 MiB / 15.5% CPU) |
478 (136 MiB / 15.5% CPU) |
360 (79 MiB / 8.6% CPU) |
360 (79 MiB / 8.6% CPU) |
473 (69 MiB / 6.4% CPU) |
473 (69 MiB / 6.4% CPU) |
473 (147 MiB / 4.7% CPU) |
473 (147 MiB / 4.7% CPU) |
475 (150 MiB / 16.2% CPU) |
475 (150 MiB / 16.2% CPU) |
| Slow consumer (256 KiB GET, throttled client read) | HTTP/2 · TLS | HTTP/2 · TLS |
8 (97 MiB / 2.5% CPU) |
8 (97 MiB / 2.5% CPU) |
Not possible | Not possible |
455 (89 MiB / 22.6% CPU) |
455 (89 MiB / 22.6% CPU) |
466 (152 MiB / 12.9% CPU) |
466 (152 MiB / 12.9% CPU) |
🥇 466 (168 MiB / 31.1% CPU) |
466 (168 MiB / 31.1% CPU) |
| Early response (origin writes after first request chunk) | HTTP/2 · TLS | HTTP/2 · TLS |
0 (112 MiB / 0.1% CPU) |
0 (112 MiB / 0.1% CPU) |
Not possible | Not possible | Not measured | Not measured |
0 (156 MiB / 19.5% CPU) |
3,680 (156 MiB / 19.5% CPU) |
0 (146 MiB / 0.2% CPU) |
0 (146 MiB / 0.2% CPU) |
| Duplex (both directions live) | HTTP/2 · TLS | HTTP/2 · TLS |
0 (112 MiB / 0.1% CPU) |
0 (112 MiB / 0.1% CPU) |
Not possible | Not possible |
0 (91 MiB / 0.1% CPU) |
0 (91 MiB / 0.1% CPU) |
0 (151 MiB / 19.4% CPU) |
3,724 (151 MiB / 19.4% CPU) |
0 (151 MiB / 0.1% CPU) |
0 (151 MiB / 0.1% CPU) |
| Duplex (WebSocket / extended CONNECT) | HTTP/1 · TLS | HTTP/1 · plain |
28,567 (125 MiB / 44.0% CPU) |
28,567 (125 MiB / 44.0% CPU) |
🥇 33,775 (100 MiB / 35.5% CPU) |
33,775 (100 MiB / 35.5% CPU) |
31,870 (84 MiB / 39.3% CPU) |
31,870 (84 MiB / 39.3% CPU) |
31,525 (127 MiB / 39.4% CPU) |
31,525 (127 MiB / 39.4% CPU) |
27,109 (125 MiB / 44.1% CPU) |
27,109 (125 MiB / 44.1% CPU) |
Slow consumer is sleep-bound; H1/H2/H3 sit in the same band. Early-response H1/H2/H3: TWP leads (H1 early ≈ 2.00× / 1.47× YARP Win/Linux). Duplex H2: YARP leads by design — Win ≈ 0.59× (1,270 / 2,135), Linux ≈ 0.15× (282 / 1,882); irreducible concurrent-copier cell (see IO model). WebSocket: TWP÷YARP Windows ≈ 1.06×; Linux nginx leads.
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.


Median of 3 repeats on windows-latest @ 9a2b3a1e. Source: Actions 34441599658. Absolute RPS on GHA swings hard; prefer TWP÷YARP.

| Workload | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|
| Keep-alive · tiny GET | 🥇 20,612 (87 MiB / 47.9% CPU) |
20,612 (87 MiB / 47.9% CPU) |
8,972 (142 MiB / 24.8% CPU) |
8,972 (142 MiB / 24.8% CPU) |
Not possible | Not possible | Not possible | Not possible |
18,392 (105 MiB / 50.9% CPU) |
18,392 (105 MiB / 50.9% CPU) |
| New-connection · tiny GET | 🥇 732 (88 MiB / 9.5% CPU) |
732 (88 MiB / 9.5% CPU) |
0 (140 MiB / 24.4% CPU) |
248 (140 MiB / 24.4% CPU) |
Not possible | Not possible | Not possible | Not possible |
725 (113 MiB / 10.7% CPU) |
725 (113 MiB / 10.7% CPU) |
| Keep-alive · 256 KiB GET | 🥇 2,741 (130 MiB / 47.0% CPU) |
2,741 (130 MiB / 47.0% CPU) |
0 (142 MiB / 24.6% CPU) |
162 (142 MiB / 24.6% CPU) |
Not possible | Not possible | Not possible | Not possible |
2,699 (136 MiB / 49.4% CPU) |
2,699 (136 MiB / 49.4% CPU) |
Median of 3 repeats @ 9a2b3a1e. Source: Actions 34441599658.

| Workload | TWP sustain | TWP peak | nginx sustain | nginx peak | HAProxy sustain | HAProxy peak | Envoy sustain | Envoy peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|---|---|
| Keep-alive · tiny GET |
36,114 (108 MiB / 49.4% CPU) |
36,114 (108 MiB / 49.4% CPU) |
🥇 44,372 (102 MiB / 40.8% CPU) |
44,372 (102 MiB / 40.8% CPU) |
43,256 (84 MiB / 43.3% CPU) |
43,256 (84 MiB / 43.3% CPU) |
27,636 (128 MiB / 57.3% CPU) |
27,636 (128 MiB / 57.3% CPU) |
32,712 (135 MiB / 49.8% CPU) |
32,712 (135 MiB / 49.8% CPU) |
| New-connection · tiny GET |
1,549 (128 MiB / 40.0% CPU) |
1,549 (128 MiB / 40.0% CPU) |
🥇 1,598 (103 MiB / 36.2% CPU) |
1,598 (103 MiB / 36.2% CPU) |
1,454 (85 MiB / 37.7% CPU) |
1,454 (85 MiB / 37.7% CPU) |
1,379 (129 MiB / 44.5% CPU) |
1,379 (129 MiB / 44.5% CPU) |
1,525 (149 MiB / 38.9% CPU) |
1,525 (149 MiB / 38.9% CPU) |
| Keep-alive · 256 KiB GET |
3,790 (126 MiB / 31.8% CPU) |
3,790 (126 MiB / 31.8% CPU) |
2,421 (101 MiB / 48.7% CPU) |
2,421 (101 MiB / 48.7% CPU) |
🥇 3,940 (84 MiB / 28.4% CPU) |
3,940 (84 MiB / 28.4% CPU) |
3,544 (145 MiB / 32.3% CPU) |
3,544 (145 MiB / 32.3% CPU) |
3,031 (160 MiB / 42.1% CPU) |
3,031 (160 MiB / 42.1% CPU) |
All three workloads are >1.00× YARP on both OS. nginx leads Linux keep-alive tiny and Linux new-connection; TWP is second, YARP third.
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 @ 9a2b3a1e — 34441548073.
| OS | Titanium | YARP | nginx | HAProxy | Envoy |
|---|---|---|---|---|---|
| Windows |
53,762 (88 MiB / 23.6% CPU) |
30,754 (123 MiB / 46.9% CPU) |
Not measured | Not possible | Not possible |
| Linux |
42,675 (120 MiB / 30.3% CPU) |
24,232 (159 MiB / 41.6% CPU) |
Not measured |
8,683 (84 MiB / 24.6% CPU) |
16,110 (128 MiB / 20.6% CPU) |
| macOS |
14,924 (94 MiB / 20.1% CPU) |
8,581 (128 MiB / 28.4% CPU) |
Not measured | Not measured | Not measured |
| 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 |
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;
};CI cadence, shards, paste scripts, and gate floors for people refreshing these tables. Consumers can skip this section.
| 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; RPC/s @ c=64 |
| Heavier tables |
compare-bodies (2 shards) / post / lossy / arch (3 shards) / tls-cost
|
dispatch independently |
See PERF-GATES.md.
--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).
- Reverse product signal: TWP÷YARP ≥ 0.70 (nginx / HAProxy / Envoy are wiki and chart peers only — no CI gate).
- MITM overhead: Lite÷Reverse ≥ 0.50 and Full÷Reverse ≥ 0.50 (median of 3 GHA runs @ c=64). Absolute RPS moves with runner heat; ratios are the claim.
- 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) ondevelopvs 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-*.csvProduct 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-saturationHarness 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).