fluxzy vs mitmproxy vs Squid, 20 months later
September 2026 update. This post was first published in January 2025 under the title "30x to 70x faster than mitmproxy/mitmdump, 4x faster than Squid", with fluxzy 1.27 and a mitmproxy from that time. Twenty months later, both tools have moved a lot, so I reran everything with current versions and measured a few things I should have measured the first time: CPU per request, memory, HTTP/2 and what happens with more connections. The original January 2025 tables are kept at the end of the article for reference.
Let me start with the number I did not have in 2025
The 2025 version of this post said fluxzy was 30x to 70x faster than mitmproxy and left it there. The obvious objection, which several readers made, is that fluxzy is multi-threaded and mitmproxy is not, so what exactly was being compared? This time I recorded the CPU time each proxy actually consumed and divided it by the requests it served.
On intercepted TLS traffic, mitmproxy spends about 580 µs of CPU per request and fluxzy about 59 µs. mitmproxy sits at 0.99 core in every run; fluxzy uses whatever cores it is given. So the headline ratio is really two facts multiplied: roughly 10x less CPU per request, times the number of cores. Both are true, both are measured below, and the second one is not fluxzy's merit. The rest of the post is the full picture: the four scenarios from 2025 rerun with current versions, then HTTP/2, more connections, large bodies, memory, and the cost of a rule.
Why this benchmark exists
Back in January 2025, a user asked me about the performance overhead of impersonation rules in fluxzy. I had never run a proper benchmark, so I set one up in a way that could be reproduced across configurations. The idea was, and still is, deliberately simple: count requests per second, nothing fancy, and compare fluxzy CLI with the tools people usually put next to it.
As a reminder, fluxzy CLI is an open-source command-line HTTP intermediary. It sits between a client and a server, decrypts TLS on the fly, and lets you record, alter and replay HTTP(S) traffic with a rule file.
Most MITM tools are closed-source, paid, or ship a license that forbids publishing comparisons. So the comparison is still fluxzy CLI against mitmproxy/mitmdump, the console-based version of mitmproxy optimized for quick traffic dumping. As a reference, I also run without any proxy, and through Squid with caching disabled. Squid is not a MITM tool: on plain HTTP it forwards requests, on HTTPS it just opens a CONNECT tunnel and relays bytes without looking at them. That makes it a useful baseline for "what does a proxy cost when it does not decrypt anything".
What changed since 2025
| January 2025 | September 2026 | |
|---|---|---|
| fluxzy | CLI v1.27, .NET 8 | Docker image fluxzy/fluxzy:1.39.48, .NET 10 |
| mitmproxy | mitmdump, early 2025 release | Docker image mitmproxy/mitmproxy:latest = 12.2.3, Python 3.14.5, OpenSSL 4.0 |
| Squid | plain HTTP only | Docker image ubuntu/squid:latest = 6.13, plain HTTP and CONNECT tunnel |
| Machine | AMD Ryzen 9 7950X3D, 16 cores, Fedora 41 | AMD Ryzen 7 9700X, 8 cores, Fedora 44 |
The machine is different and has half the cores, so absolute numbers are not comparable with the 2025 run. Ratios are. Keep that in mind when you scroll down to the old tables.
Setting up the benchmark
The setup is the same as in 2025 and lives in the floody repository:
+---------+ +-----------+ +-----------+
| floody |------>| Proxy |------>| floodys |
| (Client)| | (to test) | | (Server) |
+---------+ +-----------+ +-----------+
- floody is a small wrapper around .NET's
HttpClientthat fires requests as fast as it can over a fixed number of connections, through an optional proxy. None of the popular load tools (wrk, k6, bombardier) support proxies, hence the home-made client. - floodys is a Kestrel endpoint that returns a response body of the size you ask for. It listens on both HTTP and HTTPS and easily does 300K requests per second on this machine, so it is never the bottleneck.
New this year: the three proxies run in Docker with network_mode: host. Networking still goes through loopback exactly as before, so the container adds nothing on the data path, but the container's cgroup gives me exact CPU time and memory for each proxy. That is where the new "CPU per request" and "memory" columns come from.
Every run is 2 seconds of warm-up followed by 15 seconds of measurement. All proxies skip upstream certificate validation (fluxzy start -k, mitmdump -k -q). Squid runs with cache deny all and no access log, and the test server runs with request logging off (with Kestrel's default Information level, the no-proxy baseline drops by 38%, so this matters if you rerun it). Not a single request failed in any of the 74 runs.
One caveat before any number: everything runs on a single desktop over loopback. There is no network latency to hide behind, which is what makes the proxy overhead visible and measurable. On a real link with 10 ms of round trip, most of the differences below vanish into the noise, and a request that takes 60 µs of CPU or 600 µs of CPU looks the same to a browser. This benchmark measures a ceiling, not your day-to-day experience. It also measures throughput only: no latency percentiles, no startup time, no memory at idle, and nothing about protocol coverage or ergonomics, where mitmproxy has plenty to say.
Reproducing the tests
- Start the three proxies. fluxzy gets
--max-upstream-connection 256so the 128-connection runs are not capped by the default of 16.
docker run -d --network host -e FLUXZY_ADDRESS=127.0.0.1 fluxzy/fluxzy:1.39.48 start -k --max-upstream-connection 256
docker run -d --network host mitmproxy/mitmproxy:latest mitmdump -k -q --listen-host 127.0.0.1 -p 8080
docker run -d --network host -v $PWD/squid.conf:/etc/squid/squid.conf:ro ubuntu/squid:latest
with this squid.conf:
http_port 127.0.0.1:3128
http_access allow all
cache deny all
access_log none
- Clone floody, build the client and the server, then run the client with the same options for each proxy:
floody https://127.0.0.1:5443 -c 16 -d 15s -w 2s --http1 --http-connect -x 127.0.0.1:44344 -l 8192
-c is the number of connections, -l the response body size, -x the proxy. --http-connect and --http1 are new: floody now defaults to SOCKS5 and HTTP/2, and these two flags bring it back to plain HTTP CONNECT and HTTP/1.1, which is what the 2025 tests used. Drop them to test the HTTP/2 path.
The results
Same four scenarios as in 2025: plain HTTP and TLS, with an empty response body and with an 8 KiB one. HTTP/1.1, HTTP CONNECT, 16 concurrent connections. Values are requests per second.
| No proxy | fluxzy | mitmproxy/mitmdump | Squid | fluxzy vs mitmproxy | 2025 ratio | |
|---|---|---|---|---|---|---|
| Plain, no body | 316,980 | 140,289 | 1,874 | 39,052 | 75x | 69x |
| TLS, no body | 325,503 | 87,164 | 1,711 | 107,325 (tunnel) | 51x | 40x |
| Plain, 8 KiB body | 236,988 | 75,964 | 1,747 | 24,893 | 43x | 44x |
| TLS, 8 KiB body | 143,663 | 37,867 | 1,555 | 75,137 (tunnel) | 24x | 28x |
Fedora Linux 44, AMD Ryzen 7 9700X 8-Core Processor, all proxies in Docker on the host network
A few things to notice:
- The gap is still there and it grew on the empty-body cases. Twenty months of releases on both sides moved the ratios but not the picture.
- On plain HTTP, fluxzy does 3.6x what Squid does with no body and 3.1x with 8 KiB bodies. That was 3.9x in 2025.
- On TLS, Squid is faster than fluxzy at 16 connections. That is expected and I did not hide it this time: Squid opens a tunnel and copies bytes, fluxzy terminates TLS twice, parses HTTP on both sides and evaluates rules. The difference between the two is the cost of actually looking inside the traffic, about 20% of throughput at this connection count. More on that below, because it flips with more connections.
I ran the two TLS scenarios three times each to see how noisy this is. fluxzy lands within 3 to 5% of itself, mitmproxy within 1%. On the three-run averages the ratios are 50x and 25x.
The part I skipped in 2025: CPU per request
The first objection any reader will have is "of course, fluxzy is multi-threaded". Fair. So this time I recorded how much CPU each proxy actually burned during the 15 seconds, and divided by the number of requests it served.
| Scenario | fluxzy: cores used, CPU per request | mitmproxy: cores used, CPU per request | Squid: cores used, CPU per request |
|---|---|---|---|
| Plain, no body | 4.2 cores, 30 µs | 0.99 core, 528 µs | 0.85 core, 22 µs |
| TLS, no body | 5.2 cores, 59 µs | 0.99 core, 579 µs | 0.57 core, 5 µs |
| Plain, 8 KiB | 3.5 cores, 46 µs | 0.98 core, 563 µs | 0.75 core, 30 µs |
| TLS, 8 KiB | 3.6 cores, 96 µs | 0.98 core, 632 µs | 0.51 core, 7 µs |
Two separate facts hide in the headline ratio, and the second one is the one I find interesting. That a .NET binary beats a Python program on raw throughput is not news to anyone; what the numbers say is how much of the gap is the language and how much is the design.
- mitmproxy sits at 0.99 core in every single run. The Python event loop is pinned to one core, and 16 connections are enough to saturate it. Whatever else happens, one mitmdump process cannot go faster than one core allows. People who need more run several instances behind a load balancer, which is a perfectly valid setup, so I tested it too (see below).
- Per unit of CPU, fluxzy does about 10x more work on intercepted TLS traffic (59 µs vs 579 µs per request) and about 17x more on plain HTTP. The 25x to 75x ratio is roughly "10x per core, times the cores you have".
The Squid column is a nice sanity check. On plain HTTP, Squid spends 22 µs per request and fluxzy 30 µs. Squid is a C program that reads headers and forwards. fluxzy parses the exchange, runs the rule engine and records metadata, for 40% more CPU. I can live with that.
HTTP/2 doubles it
fluxzy speaks HTTP/2 to the client since version 1.35, but it is off by default; you enable it with --serve-h2. mitmproxy speaks HTTP/2 to the client out of the box. floody now requests HTTP/2 by default, so I ran the TLS scenarios again without --http1, and once with --serve-h2 on the fluxzy side.
| TLS, 16 connections | No proxy | fluxzy (HTTP/1.1) | fluxzy --serve-h2 |
mitmproxy (HTTP/2) | Squid tunnel |
|---|---|---|---|---|---|
| No body | 306,404 | 88,071 | 167,770 | 1,634 | 306,897 |
| 8 KiB body | 94,667 | 39,086 | 75,267 | 1,339 | 94,342 |
- One flag, twice the throughput. With
--serve-h2, fluxzy does 168K intercepted TLS requests per second on an 8-core desktop and CPU per request drops from 58 to 29 µs. - With HTTP/2 on both sides the ratio is 103x with no body and 56x with 8 KiB bodies. On 8 KiB bodies fluxzy is at 80% of the no-proxy rate while decrypting and re-encrypting everything.
- mitmproxy gets slightly slower when the client negotiates HTTP/2 (1,711 to 1,634 and 1,555 to 1,339).
- HTTP/2 is not free on the fluxzy side either: memory goes from about 67 MiB to about 120 MiB.
If you run fluxzy in front of anything that talks HTTP/2, turn --serve-h2 on. It is still opt-in because client-facing HTTP/2 is recent (March 2026) and I would rather have people enable it knowingly for now. Expect it to become the default.
More connections
Same TLS, no body, HTTP/1.1 scenario with 16, 64 and 128 client connections.
| Connections | No proxy | fluxzy | mitmproxy | Squid tunnel |
|---|---|---|---|---|
| 16 | 325,503 | 87,164 | 1,711 | 107,325 |
| 64 | 295,331 | 111,037 | 1,728 | 109,938 |
| 128 | 284,400 | 126,280 | 1,670 | 110,960 |
fluxzy is the only one of the three that benefits from more connections (+45% from 16 to 128). mitmproxy is flat because its single core is already full. Squid is flat too, at about 110K, while using only 0.6 core, so it is bound by its event loop latency rather than by CPU. At 128 connections fluxzy, which decrypts and re-encrypts every byte, is faster than Squid, which does not even look at them.
Memory stays reasonable for everyone: fluxzy goes from 66 to 88 MiB, mitmproxy from 80 to 113 MiB, Squid from 28 to 38 MiB.
Scaling mitmproxy the way people actually do
"mitmproxy is single core" is only half an answer, because nobody who needs throughput runs one mitmdump. They run several and put a TCP load balancer in front. So I did that: 8 mitmdump instances (one per physical core on this machine) behind HAProxy 3.2 in TCP mode with round-robin, and the client pointed at HAProxy. Same TLS, no body, HTTP/1.1 over CONNECT. The CPU and memory columns are the sum of HAProxy and all instances.
| TLS, no body | Requests/s | Cores used | CPU per request | Memory |
|---|---|---|---|---|
| 1 mitmdump, 16 connections | 1,711 | 0.99 | 579 µs | 80 MiB |
| 4 mitmdump + HAProxy, 64 connections | 5,626 | 4.0 | 715 µs | 331 MiB |
| 8 mitmdump + HAProxy, 16 connections | 8,132 | 7.9 | 975 µs | 595 MiB |
| 8 mitmdump + HAProxy, 64 connections | 8,642 | 8.0 | 927 µs | 624 MiB |
| 8 mitmdump + HAProxy, 128 connections | 8,738 | 8.0 | 918 µs | 647 MiB |
| fluxzy, 64 connections, same run | 108,156 | 4.2 | 39 µs | 84 MiB |
With 8 KiB bodies and 64 connections: 7,502 req/s for the 8 instances (1,068 µs per request), 52,651 for fluxzy (77 µs).
- It works, and it is the right way to run mitmproxy under load: 5x the throughput of a single instance with 8 of them, 3.3x with 4.
- It does not scale linearly. Eight instances burn eight full cores but deliver 5x, because CPU per request goes up from 579 to 927 µs. Part of that is SMT: this is an 8-core, 16-thread CPU, and the instances now compete with each other, with the client and with the server for the same physical cores. On a bigger machine the curve would be kinder to mitmproxy; on this one, it is what a real deployment on an 8-core box would see.
- HAProxy itself is almost free: 0.14 core and 16 µs per request, measured separately.
- Memory is 8 times a mitmdump, as you would expect: about 620 MiB for the fleet, versus 84 MiB for one fluxzy.
Compared with fluxzy at the same 64 connections, the gap goes from 51x to 12.5x on requests per second, and from 10x to about 24x on CPU per request, since fluxzy did its 108k with 4.2 cores while the fleet used 8. This is the fairest throughput comparison in the article, and it is the one I would quote if someone asked me "but what if I scale mitmproxy out".
Big bodies and memory
The 2025 post claimed that fluxzy is "always streaming" and never holds a full response in memory. A claim is cheap, so here is the measurement: 1 MiB response bodies, TLS, 16 connections, HTTP/1.1. The "delivered" column is simply requests per second times 1 MiB.
| Requests/s | Body delivered | Peak memory | |
|---|---|---|---|
| No proxy | 4,686 | 4.6 GiB/s | |
| Squid tunnel | 2,931 | 2.9 GiB/s | 53 MiB |
| fluxzy | 1,486 | 1.45 GiB/s | 88 MiB |
| mitmproxy, default settings | 393 | 393 MiB/s | 524 MiB |
mitmproxy, stream_large_bodies=64k |
363 | 363 MiB/s | 101 MiB |
- fluxzy moves 1 MiB bodies 3.8x faster than mitmproxy while decrypting both legs, at 88 MiB of memory.
- mitmproxy buffers whole bodies by default. With sixteen 1 MiB responses in flight, that is a 524 MiB peak. Its
stream_large_bodiesoption brings memory back to 101 MiB but does not make it faster; it is actually a bit slower. - With
--serve-h2, fluxzy does 1,575 requests per second on the same test, at 196 MiB.
What does a rule cost?
This is the question that started the whole thing in 2025, and the answer has not changed: nothing measurable. I added a rule that sets one response header on every exchange, on both tools, TLS with 8 KiB bodies.
| Requests/s | vs the same tool without the rule | |
|---|---|---|
fluxzy, YAML rule addResponseHeaderAction |
38,899 | within noise |
mitmproxy, --modify-headers "/~s/X-Bench/1" |
1,544 | about -1% |
mitmproxy, Python addon with a response() hook |
1,525 | about -2% |
To be fair to mitmproxy, its scripting is not slow. Its per-rule cost is as negligible as fluxzy's. The gap you see in this article exists before any rule runs; it is the cost of the HTTP and TLS machinery itself.
A note on the bandwidth column
You will notice the tables above report requests per second and not MB/s, unlike the 2025 ones. floody counts bytes on the stream it gets after connecting. Without a proxy, or through SOCKS5, that is the decrypted stream. Through HTTP CONNECT, it is the tunnel, so TLS record overhead is counted too. The 2025 tables had a bandwidth column with this quirk baked in (it did not affect the ratios, both proxies used CONNECT). This time I only compare requests per second, and derive bandwidth from it where it matters.
Where does the gap come from?
The analysis from 2025 still holds, so I will keep it short.
- Runtime. mitmproxy is Python on a single-threaded event loop. fluxzy is .NET 10, which has had several years of performance work on sockets, TLS and the garbage collector, and it uses every core it is given.
- Single buffer, always streaming. A request is processed with one buffer; response bodies larger than that buffer are streamed and never sit in user-space memory. The 1 MiB test above is that design choice, measured.
- Rules compile to code paths, not scripts. A fluxzy rule file maps to predefined actions implemented in compiled code. Both tools showed negligible per-rule cost in this test, but there is no interpreter on the hot path.
- TLS is not the differentiator. Both use OpenSSL on Linux. What differs is everything around it.
Final words
Are these results important? For most people, still no. mitmproxy has been around for well over a decade, it is written in a language everyone can extend, its addon API is a joy, and 1,500 requests per second is more than a debugging session will ever throw at it. I remain a mitmproxy enjoyer.
Where it matters is when the proxy is not a debugging tool but a component: synthetic monitoring at scale, a rules-based gateway in front of a fleet of agents, capturing traffic from a load test without becoming the bottleneck of the load test. That is what fluxzy is built for, and that is what the numbers above are about. If you are in that situation, the two things to remember from this post are: fluxzy spends about 10x less CPU per intercepted request, and --serve-h2 doubles what it does with that CPU.
As in 2025, this was not done in laboratory conditions: one desktop, loopback, 15 seconds at a time. It is designed to give a rough but reproducible picture. Everything you need to rerun it is above, and if your numbers disagree with mine, I would genuinely like to hear about it.
The original January 2025 results
Kept for reference. These were measured with fluxzy CLI v1.27.3.1 on .NET 8 and the mitmdump release of the time, on an AMD Ryzen 9 7950X3D under Fedora 41, HTTP/1.1 over HTTP CONNECT, 16 connections, 15 seconds. The bandwidth column has the counting quirk explained above.
PLAIN - No response body - 15s
| Total | Success | Fail | req/s | Bandwidth | |
|---|---|---|---|---|---|
| No proxy | 4035035 | 4035031 | 0 | 269002.0 | 33.86 MB/s |
| squid | 389874 | 389874 | 0 | 25991.6 | 5.4 MB/s |
| fluxzy | 1525442 | 1525442 | 0 | 101696.1 | 12.8 MB/s |
| mitmproxy/mitmdump | 22064 | 22064 | 0 | 1470.9 | 189.61 KB/s |
| Diff. MITM | 69 times | 69 times | / | 69 times | 69 times |
TLS - No response body - 15s
| Total | Success | Fail | req/s | Bandwidth | |
|---|---|---|---|---|---|
| No proxy | 3317020 | 3317020 | 0 | 221134.667 | 27.84 MB/s |
| fluxzy | 852732 | 852732 | 0 | 56848.800 | 15.51 MB/s |
| mitmproxy/mitmdump | 20994 | 20994 | 0 | 1399.600 | 392.9 KB/s |
| Diff. MITM | 40 times | 40 times | / | 40 times | 40 times |
PLAIN - 8192 bytes response body - 15s
| Total | Success | Fail | req/s | Bandwidth | |
|---|---|---|---|---|---|
| No proxy | 2669850 | 2669850 | 0 | 177990.000 | 1.38 GB/s |
| squid | 228279 | 228279 | 0 | 15218.600 | 122.49 MB/s |
| fluxzy | 930238 | 930238 | 0 | 62015.867 | 493.61 MB/s |
| mitmproxy/mitmdump | 20860 | 20860 | 0 | 1390.667 | 11.07 MB/s |
| Diff. MITM | 44 times | 44 times | / | 44 times | 44 times |
TLS - 8192 bytes response body - 15s
| Total | Success | Fail | req/s | Bandwidth | |
|---|---|---|---|---|---|
| No proxy | 1822330 | 1822330 | 0 | 121488.667 | 966.97 MB/s |
| fluxzy | 532140 | 532134 | 0 | 35475.600 | 568.46 MB/s |
| mitmproxy/mitmdump | 18784 | 18784 | 0 | 1252.267 | 20.02 MB/s |
| Diff. MITM | 28.329 times | 28.329 times | / | 28.329 times | 28.401 times |
Fedora Linux 41 (Workstation Edition) **AMD Ryzen 9 7950X3D 16-Core Processor