Setup
Germany ↔ Netherlands (about 36 ms apart), +20 ms delay each way, 100 Mbit/s cap, random loss in both directions; test website on BBR (host default); 3 runs per loss level in shuffled order; page load = 60 requests of 100 KB.
Two Linux servers, one acting as the client and one running the VPN servers and a small test website. The poor network is made with Linux tc netem on the client: the same loss, delay and speed cap apply in both directions, and only to packets exchanged with the lab server. Loss is per direction, so 5 % each way is roughly 10 % over a round trip.
| Protocol | Carried over | How it carries traffic |
|---|---|---|
| WireGuard | UDP | IP tunnel: your own TCP connections travel inside it end to end. |
| OpenVPN (UDP mode) | UDP | IP tunnel, the same idea as WireGuard. |
| VLESS Reality | TCP | Proxy: TCP ends at the VPN server and is re-sent over one TLS-looking TCP connection. |
| Hysteria2 | QUIC (UDP) | Proxy over QUIC with its own congestion control. |
| No VPN | — | Reference: the same request without a tunnel. |
All protocols run with standard, documented settings; none gets special tuning. Hysteria2 runs without bandwidth hints, its default, in which case it uses BBR.
What was measured
- Download speed: one 64 MB file, capped at 20 seconds, average Mbit/s.
- Page loads: twenty requests of 100 KB per run, each with a 10-second timeout. Every load time is recorded and failures are counted. This is closer to how browsing feels than a speed test.
- Every loss level and protocol was run three times, in shuffled order; the tables show medians.
Reading the results
Why the IP tunnels fall so far. WireGuard and OpenVPN do not recover lost packets; the TCP connection inside the tunnel has to, end to end, across the whole lossy path. VLESS Reality ends your TCP connection at the VPN server, so recovery happens on the server's own TCP connection, and Hysteria2 recovers losses inside QUIC with its own congestion control.
Why Hysteria2's pages load so fast. Mostly because QUIC keeps one connection open and opens a new stream per request, while the other paths start a fresh TCP connection for every request — and, for VLESS Reality, a fresh Reality handshake too. It is a property of connection reuse as much as of loss handling.
Hysteria2 at 10 %. It kept 91 % of its speed at 5 % loss but 47 % at 10 %, while VLESS Reality kept 78 %. This run shows the drop, not its cause.
Limits — read before quoting
- The test website used BBR (the host default). Inside WireGuard and OpenVPN your TCP connection is controlled by the website's server, and BBR shrugs off random loss far better than CUBIC, the Linux default. This is the most forgiving case for TCP inside a tunnel; with CUBIC the tunnel numbers can be lower.
- Loss was random and independent. Real Wi-Fi and mobile loss often comes in bursts, which can change the ranking.
- One route, one pair of servers. Other distances, CPUs or providers give other absolute numbers. Compare protocols within this run, not with numbers from another setup.
- This is not a censorship test. Every protocol was allowed through. How they behave when UDP is blocked or traffic is inspected is what the continuous measurements and later experiments are for.
- Speed was capped at 100 Mbit/s on purpose, so the test measures behaviour, not server CPU.
Failed runs
One OpenVPN download at 1 % loss and one VLESS Reality download at 5 % loss failed to start. They are counted as 0 Mbit/s; with three runs per cell the medians are unaffected. A handful of page requests failed at 1 % and 10 %; they are in the raw data.
Repeat it
The scripts install the servers, apply the network conditions, run the measurement loop and summarise the results. You need two Linux servers, ideally in different cities; a full run takes about an hour. Everything is at github.com/colitu/vpn-lab (scripts under the MIT licence), and the raw results of this run are in results/ep01-packet-loss.jsonl.
The experiment measures how protocols behave, not which VPN provider is better. Colitu's apps switch between protocols automatically when a network gets bad, which is why we care how each one survives. See the methodology on what that means for our results.