Reverse Proxy Benchmark 2025: NPM vs. NPM+ (HTTP/3) vs. Zoraxy
A detailed performance benchmark comparing Nginx Proxy Manager, NPM+ with HTTP/3, and the newcomer Zoraxy. Which reverse proxy is the fastest and easiest for your setup?
Table of Contents
Why Use a Reverse Proxy? The Basics Explained
In the world of home servers and self-hosting, we often juggle numerous applications, each accessible on a different port. Remembering addresses like 192.168.1.10:8080, 192.168.1.10:9000, and 192.168.1.10:3000 is cumbersome and insecure.
This is where a Reverse Proxy comes in. It acts as a central gatekeeper for all incoming requests. Instead of memorizing dozens of ports, we can simply access a subdomain like service.your-domain.com. The reverse proxy receives the request, handles SSL/TLS encryption (the https in the address bar), and forwards it internally to the correct service and port.
In a previous video, I compared several reverse proxies. Today, we’re taking it a step further by putting three modern, visually-oriented contenders through a performance benchmark:
- Nginx Proxy Manager (NPM): The popular classic, serving as our stable baseline.
- NPM+: A community fork of NPM, where we will specifically enable HTTP/3.
- Zoraxy: A new but extremely powerful and modern challenger.
We’ll install all three using Docker and put their performance to the test with the tools wrk and k6.
Glossary: Key Terms and Technologies
| Term | Description |
|---|---|
| Docker | A platform for creating and running applications in isolated environments called containers. |
| Reverse Proxy | A server that accepts requests from the internet and forwards them to one or more internal servers. |
| Nginx Proxy Manager | An easy-to-use reverse proxy with a graphical user interface (UI), based on Nginx. |
| NPM+ | A fork of NPM that offers more modern features, such as HTTP/3 support. |
| Zoraxy | A modern reverse proxy written in Go, designed for high performance and ease of use. |
| HTTP/3 | The latest version of the HTTP protocol, based on QUIC, designed to reduce latency, especially on poor networks. |
| QUIC | A transport protocol developed by Google that runs on UDP instead of TCP and forms the basis for HTTP/3. |
| wrk | A simple command-line tool for HTTP stress testing, designed for high throughput. |
| k6 | A modern, scriptable load testing tool for simulating realistic user behavior under various load scenarios. |
The Preparation: Our Test Setup
For a fair comparison, we need a consistent test environment.
1. Installing Server Tools
I am renting four servers from Hetzner. On three of them, I install Docker:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
On the fourth server, I install wrk and k6:
sudo apt install wrk -y
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
Alternatively, this can be done using a Cloud-Init-Config.
2. A Common Network for All Containers
To allow our proxies to communicate with the backend service, we create a common Docker network on each of the three servers.
sudo docker network create proxy
3. The Backend Service: Homepage
As the target service for our proxies, we use the gethomepage/homepage dashboard. It’s lightweight and perfect for our test.
homepage/docker-compose.yml:
services:
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
environment:
- HOMEPAGE_ALLOWED_HOSTS=your.domain.com # ADJUST HERE
- PUID=1000
- PGID=1000
volumes:
- ./config:/app/config
networks:
- proxy
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
networks:
proxy:
external: true
We start this container with sudo docker compose up -d. It has no public port – access is exclusively through our reverse proxies.
The Contenders in Detail
1. Nginx Proxy Manager (NPM)
The classic. Known for its extremely simple, web-based configuration.
npm/docker-compose.yml:
services:
app:
image: 'jc21/nginx-proxy-manager:2.12.6'
container_name: npm
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- proxy
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
networks:
proxy:
external: true
We create the data and letsencrypt folders. After starting with docker compose up -d, we log in at http://<SERVER_IP>:81 and create a proxy host pointing to the homepage service on port 3000. The SSL certificate is automatically obtained via Let’s Encrypt.
2. NPM+ (with HTTP/3)
The modernized version of NPM. The key feature here is enabling HTTP/3.
npm-plus/docker-compose.yml:
services:
app:
image: 'ghcr.io/zoeyvid/npmplus:latest'
container_name: npm-plus
restart: unless-stopped
ports:
- '80:80/tcp'
- '443:443/tcp'
- '443:443/udp'
- '81:81/tcp'
environment:
- 'PUID=1000'
- 'PGID=1000'
- 'TZ=Europe/Berlin'
# To enable HTTP/3, you have to enable it in the settings page of the UI
volumes:
- ./data:/data
networks:
- proxy
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
networks:
proxy:
external: true
The crucial difference is exposing port 443 for UDP. The configuration in the UI is identical to NPM, with an additional toggle in the settings to enable HTTP/3. The login password can be found in the logs: sudo docker compose logs.
3. Zoraxy
A modern challenger written in Go, focusing on performance and a clean UI. HTTP/3 is active by default.
zoraxy/docker-compose.yml:
services:
zoraxy:
image: tobiaskoch/zoraxy
container_name: zoraxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3 is on by default
- "8000:8000"
volumes:
- ./config/:/opt/zoraxy/config/
- ./plugin/:/opt/zoraxy/plugin/
- /etc/localtime:/etc/localtime:ro
networks:
- proxy
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
networks:
proxy:
external: true
Here, we create the config and plugin folders. After starting, we can access the admin UI at http://<SERVER_IP>:8000. Setting up a proxy host is just a few clicks away.
The Benchmark: The Results
I conducted two tests from the fourth server:
wrk: A raw stress test with 100 concurrent connections for 1 minute to measure maximum throughput (version 4.1.0).k6: A dynamic scenario that slowly ramps up the load, holds it, and simulates load spikes to test realistic behavior (version 1.1.0).
wrk Results: Raw Power
wrk -t4 -c100 -d60s --latency your.domain.com
| Metric | Nginx Proxy Manager | NPM+ (with HTTP/3) | Zoraxy |
|---|---|---|---|
| Requests/Second | 91 | 102 | 155 |
| Average Latency | 287 ms | 245 ms | 77 ms |
| p99 Latency | 605 ms | 413 ms | 263 ms |
| Errors (Timeouts) | 0 | 0 | 701 (0.02%) |
The wrk results are clear: Zoraxy is in a league of its own in terms of speed. It handles 50% more requests than Nginx Proxy Manager at a fraction of the latency. BUT: under this extreme, continuous fire, Zoraxy started to drop requests (701 timeouts). NPM and NPM+ remained perfectly stable.
k6 Results: Realistic Scenario
// The k6 script used for testing
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 }, // Phase 1: Ramp-up
{ duration: '1m', target: 50 }, // Phase 2: Soak
{ duration: '30s', target: 200 }, // Phase 3: Stress
{ duration: '1m', target: 200 }, // Phase 4: Soak
{ duration: '30s', target: 0 }, // Phase 5: Ramp-down
],
thresholds: {
'http_req_duration': ['p(95)<800'],
'http_req_failed': ['rate<0.01'],
'checks': ['rate>0.99'],
},
};
// ... rest of the script ...
| Metric | NPM | NPM+ | Zoraxy |
|---|---|---|---|
| Total Requests | 22,429 | 22,278 | 22,460 |
Response Time p(95) | 13.1 ms | 27.6 ms | 10.7 ms |
| Error Rate | 0% | 0% | 0% |
The dynamic k6 test paints a different picture. Here, all three contenders are perfectly stable and deliver error-free results. Zoraxy is again the fastest, closely followed by the classic NPM. NPM+ shows a slight performance overhead, likely due to the enabled HTTP/3 processing for the test tool’s HTTP/1.1 requests.
Final Verdict and Recommendation
So, which reverse proxy should you choose? It depends on your priorities.
-
For Beginners and Stability Fans: Nginx Proxy Manager
It’s the rock-solid choice. Incredibly easy to use and absolutely reliable in every scenario. The perfect, worry-free option. -
For the Modern All-Rounder: NPM+
The ideal evolution. You get a familiar UI and modern features like HTTP/3 that can be enabled with a single click. The best compromise between stability, features, and ease of use. -
For Performance Hunters: Zoraxy
Thek6test proved that Zoraxy is also perfectly stable in realistic scenarios. Its potential is undeniable, making it a contender you should definitely keep an eye on.
What exactly is a reverse proxy?
Why should I use Docker for installation?
What are the real-world benefits of HTTP/3?
Is Zoraxy unstable because of the errors in the wrk test?
Can I run these reverse proxies on a Synology NAS or Raspberry Pi?
Related Articles
PostgreSQL vs. MariaDB vs. SQLite: A Performance Test
Which database is best for your application? A detailed performance benchmark comparison between PostgreSQL, MariaDB, and SQLite.
Ubuntu 24.04 vs. Debian 13: Docker Performance Benchmark
Is Debian really leaner and faster than Ubuntu? I pitted both operating systems against each other as Docker hosts using a benchmark script.
Install Nginx Proxy Manager on Synology NAS
Learn how to install Nginx Proxy Manager on your Synology NAS and assign it a dedicated IP address to avoid port conflicts.
SSL for Nginx in Docker: How to Set Up PFX Certificates Correctly
A practical guide for self-hosters: How to convert PFX certificates to PEM and securely set up SSL/TLS for your Nginx container in Docker.