A high-performance proxy server written in Rust with load balancing, Shadowsocks/VMess listeners, and a real-time web dashboard.
This project was inspired by the limitations of HAProxy + SOCKS load balancing setups, which proved to be unstable in production environments. Existing solutions lacked strong backend disable mechanisms and operational visibility. RustProxy focuses on stability, throughput, and day-2 ops (dashboard, healthchecks, self-update, self-bench).
- TCP Proxy: High-speed byte-forwarding to a target (or LB backends)
- TCP Load Balancing: Round-robin or random across multiple backends; admin drain/kill
- Shadowsocks Server: AEAD ciphers (standalone or combined with TCP LB)
- VMess listener: AEAD mode on a separate port (TCP LB path)
- HTTP Proxy: Forward proxy + HTTPS
CONNECTtunnels - SOCKS5 Proxy: CONNECT + UDP ASSOCIATE, optional user/pass auth
- Web Dashboard: LB stats, enable/disable backends, traffic history
- Healthcheck: TCP or SOCKS5 probes; disable after consecutive failures (drain, no kill)
- Self-update:
rustproxy --updatefrom GitHub releases - Self-bench:
rustproxy --benchlocalhost loopback throughput (direct/tcp/socks5/http) - DNS: Custom UDP/TCP/DoT/DoH resolvers with multi-IP connect fallback
Downloads the latest release binary into the current directory. Auto-detects Linux/macOS and x86_64/arm64.
curl -fsSL https://raw.githubusercontent.com/c2h2/rustproxy/master/scripts/install.sh | bashInstall elsewhere or pin a version:
curl -fsSL https://raw.githubusercontent.com/c2h2/rustproxy/master/scripts/install.sh | INSTALL_DIR=/usr/local/bin bash
curl -fsSL https://raw.githubusercontent.com/c2h2/rustproxy/master/scripts/install.sh | VERSION=v1.0.0 bashSupported targets: linux-amd64, linux-arm64 (musl, static), macos-arm64.
Re-download the latest GitHub release for this platform and replace the running binary in place (always downloads — never skips):
rustproxy --update
rustproxy --versionBuilt-in throughput test. Spins up an in-process sink/source server and
measures upload + download through each mode on 127.0.0.1:
| mode | what is measured |
|---|---|
direct |
client → backend (no proxy baseline) |
tcp |
client → TCP forward proxy → backend |
socks5 |
client → SOCKS5 CONNECT → backend |
http |
client → HTTP CONNECT tunnel → backend |
ss |
client → Shadowsocks (AES-256-GCM) → backend |
rustproxy --bench
rustproxy --bench --size 512
rustproxy --bench --modes tcp,socks5,ss --size 128 --warmup 1MB/s is decimal megabytes/sec (bytes / 1e6 / seconds). Loopback is noisy
and is an upper bound, not a WAN/NIC estimate — run --bench on your own
machine for comparable numbers.
| Host | Apple M3 Pro, 12 cores, macOS, arm64 |
| Build | rustproxy 2.1.1 release |
| Command | rustproxy --bench --size 256 --warmup 1 |
| Payload | 256 MiB upload + 256 MiB download, single stream, 127.0.0.1 |
| mode | upload MB/s | download MB/s | notes |
|---|---|---|---|
| direct | 8654 | 11896 | no proxy (baseline) |
| tcp | 9647 | 5422 | plain TCP forward |
| socks5 | 10258 | 5535 | SOCKS5 CONNECT |
| http | 10073 | 5476 | HTTP CONNECT tunnel |
| ss | 2002 | 2407 | Shadowsocks AES-256-GCM |
Rough Gbps (×8): plain proxy modes ~40–80+ Gbps on loopback; SS AES-256-GCM ~16–19 Gbps (crypto-bound). Absolute numbers vary run-to-run; relative ranking (plain ≫ SS) is stable.
Make sure you have Rust installed, then build the project:
cargo build --releaserustproxy --listen <address:port> [--target <address:port>] --mode <tcp|http|socks5|ss> [options]
rustproxy --bench [--size MiB] [--modes direct,tcp,socks5,http,ss]
rustproxy --update-
--listen <address:port>- Address to listen on -
--target <address:port>- Address to proxy requests to (required for tcp mode). Comma-separated for load balancing -
--mode <tcp|http|socks5|ss>- Proxy mode -
--cache-size <size>- Legacy CLI size (outbound stream pooling is disabled by design; kept for compatibility). Examples:0,256kb,1mb -
--buffer-size <size>- Max per-direction pump buffer (default 4mb). Buffers are adaptive: each direction starts at 8 KiB, doubles whenever a read fills the buffer, and halves after ~10 s idle — so idle tunnels cost ~16 KiB while bulk streams grow to full size. Total pump memory across all connections is hard-capped at 1 GiB; once spent, pumps stop growing instead of failing. -
--limit-per-ip-mb <mb>- Per-client-IP speed limit in MB/s; fractions allowed (0.1= 100 KB/s,1,1000). All connections from the same IP share one token bucket (upload + download combined, one second of burst), so parallel connections can't multiply the limit. Applies to every relay mode. Default: unlimited. -
--tcp-keepalive-time <secs>- Keepalive idle before first probe (default 120) -
--tcp-keepalive-interval <secs>- Keepalive probe interval (default 30) -
--tcp-keepalive-retries <n>- Unanswered probes before drop (default 3) -
--tcp-user-timeout <secs>- LinuxTCP_USER_TIMEOUT; 0 disables (default 0) -
--tcp-sndbuf/--tcp-rcvbuf-SO_SNDBUF/SO_RCVBUF(default 4mb) -
--socks5-auth <user:pass>- SOCKS5 authentication credentials (optional) -
--ss-password <password>- Shadowsocks pre-shared key (required forssmode, optional fortcpmode) -
--ss-method <cipher>- Shadowsocks cipher (default:aes-256-gcm). Supported:aes-128-gcm,aes-256-gcm,chacha20-ietf-poly1305 -
--ss-listen-port <addr:port>- Separate SS listener port (tcp mode). Plain TCP on--listen, SS on this port -
--lb <random|roundrobin>- Load balancing algorithm (tcp mode, requires multiple targets) -
--http-interface <addr:port>- HTTP dashboard for LB monitoring (e.g.:8888) -
--healthcheck- Enable healthcheck for TCP LB backends (60s interval; drain on failure) -
--healthcheck-probe <tcp|socks5>- Probe kind (default: tcp, or socks5 when SS/VMess listeners set) -
--traffic-log <path>- CSV file for persistent traffic history (default:./rustproxy_traffic.csv) -
--manager-addr <addr:port>- Manager address for stats reporting -
--dns <servers>- Custom DNS resolvers (overrides system DNS for all outbound lookups). Comma-separated list of one or more upstreams. Each entry may be:8.8.8.8— UDP on port 538.8.8.8:53— UDP on explicit portudp://1.1.1.1— UDP (explicit prefix)tcp://1.1.1.1— DNS over TCP (port 53 default)tls://1.1.1.1— DNS-over-TLS (DoT, port 853 default; bare IPs work with public resolvers like1.1.1.1/8.8.8.8/9.9.9.9whose certs carry IP SANs; hostnames liketls://dns.googlealso accepted)https://cloudflare-dns.com/dns-query— DNS-over-HTTPS (DoH; hostname required so the TLS cert validates — bare-IP DoH URLs are rejected)
Queries retry up to 3 times with a 3s per-try timeout; connect tries all resolved IPs until one accepts.
-
--dns-cache-size <N>- Max cached DNS entries when--dnsis set (default:16384, hard cap:262144). Entries respect DNS TTL; only useful working-set size is bounded. -
--update- Re-download latest GitHub release binary into place -
--bench- Localhost loopback throughput suite (see above) -
--version/-V- Print version and exit
Admin disable API: POST /api/backends/:id/disable drains in-flight connections;
append ?kill=1 to abort them immediately.
TCP Proxy:
rustproxy --listen 127.0.0.1:8080 --target 192.168.1.100:9000 --mode tcpTCP Proxy with larger pump buffer:
rustproxy --listen 127.0.0.1:8080 --target 192.168.1.100:9000 --mode tcp --buffer-size 1mbTCP Load Balancer (round-robin across 3 backends):
rustproxy --listen 127.0.0.1:8080 \
--target 192.168.1.100:9000,192.168.1.100:9001,192.168.1.100:9002 \
--mode tcp --lb roundrobin --http-interface :8888TCP Load Balancer (random algorithm, 1MB cache):
rustproxy --listen 127.0.0.1:8080 \
--target 10.0.0.1:3000,10.0.0.2:3000,10.0.0.3:3000 \
--mode tcp --lb random --cache-size 1mb --http-interface :8888TCP Load Balancer with SOCKS5 healthcheck:
rustproxy --listen 127.0.0.1:8080 \
--target 10.0.0.1:1080,10.0.0.2:1080,10.0.0.3:1080 \
--mode tcp --lb roundrobin --http-interface :8888 --healthcheckHTTP Proxy (local server, no forwarding):
rustproxy --listen 127.0.0.1:8080 --mode httpSOCKS5 Proxy (no authentication):
rustproxy --listen 127.0.0.1:1080 --mode socks5SOCKS5 Proxy with authentication:
rustproxy --listen 127.0.0.1:1080 --mode socks5 --socks5-auth username:passwordSOCKS5 Proxy with custom cache size:
rustproxy --listen 127.0.0.1:1080 --mode socks5 --cache-size 2mbShadowsocks Server (standalone):
rustproxy --listen 0.0.0.0:8388 --mode ss --ss-password mypassword --ss-method aes-256-gcmShadowsocks + TCP Load Balancer (SS decryption with LB to backends):
rustproxy --listen 0.0.0.0:11180 \
--target 127.0.0.1:10800,127.0.0.1:10801,127.0.0.1:10802,127.0.0.1:10803,127.0.0.1:10804,127.0.0.1:10805,127.0.0.1:10809,127.0.0.1:10808 \
--mode tcp --ss-password mypassword --ss-method aes-256-gcm \
--http-interface 0.0.0.0:62088 --healthcheckShadowsocks + TCP Load Balancer (separate ports for plain TCP and SS):
rustproxy --listen 0.0.0.0:11180 \
--target 127.0.0.1:10800,127.0.0.1:10801,127.0.0.1:10802 \
--mode tcp --ss-password mypassword --ss-method aes-256-gcm \
--ss-listen-port 11181 --http-interface 0.0.0.0:62088 --healthcheckThis gives port 11180 for plain TCP load balancing and port 11181 for SS clients, both routing to the same backends.
In SS+TCP LB mode, rustproxy accepts encrypted Shadowsocks client connections, decrypts the traffic, then load-balances across the backend targets. Connect with any standard SS client:
# Start a local SOCKS5 proxy that tunnels through the SS server
sslocal -b 127.0.0.1:1080 -s <server-ip>:11180 -k mypassword -m aes-256-gcm
# Then use the local SOCKS5 proxy
curl -x socks5h://127.0.0.1:1080 http://example.comUseful when the system resolver is failing (failed to lookup address information), when you want to bypass a captive resolver, or when you need DoH for privacy.
HTTP proxy with Google + Cloudflare UDP DNS (failover):
rustproxy --listen 127.0.0.1:8080 --mode http --dns 8.8.8.8,1.1.1.1SOCKS5 proxy with DNS-over-HTTPS:
rustproxy --listen 127.0.0.1:1080 --mode socks5 \
--dns https://cloudflare-dns.com/dns-query,https://dns.google/dns-queryTCP LB with mixed UDP + DoH upstreams:
rustproxy --listen 127.0.0.1:8080 \
--target backend1.example.com:443,backend2.example.com:443 \
--mode tcp --lb roundrobin \
--dns 1.1.1.1,https://dns.google/dns-queryHTTP proxy using a non-standard UDP DNS port:
rustproxy --listen 127.0.0.1:8080 --mode http --dns 9.9.9.9:53,udp://149.112.112.112HTTP proxy with DNS-over-TLS (encrypted DNS, redundant upstreams):
rustproxy --listen 127.0.0.1:8080 --mode http --dns tls://1.1.1.1,tls://8.8.8.8DNS over TCP (e.g. when UDP/53 is blocked):
rustproxy --listen 127.0.0.1:8080 --mode http --dns tcp://1.1.1.1,tcp://8.8.8.8When --dns is set, the system resolver is not used — every hostname (TCP, SOCKS5, HTTP, Shadowsocks) is resolved through the configured upstreams in order.
- TCP Proxy (
src/tcp_proxy.rs): Handles raw TCP connection forwarding, supports single-target, load-balanced, and SS-encrypted modes - Shadowsocks Proxy (
src/ss_proxy.rs): Standalone Shadowsocks server using theshadowsockscrate for AEAD decryption - Load Balancer (
src/lb.rs): Round-robin and random algorithms, per-backend atomic stats, runtime enable/disable - Healthcheck (
src/healthcheck.rs): HTTP ping backend health probing with automatic disable/re-enable - Web Dashboard (
src/web.rs): Axum-based HTTP server serving the LB dashboard and REST API - Dashboard UI (
static/lb_dashboard.html): HAProxy-style web interface with auto-refresh - HTTP Proxy (
src/http_proxy.rs): Handles HTTP request/response forwarding - SOCKS5 Proxy (
src/socks5_proxy.rs): Full SOCKS5 server implementation with authentication support - Connection Cache (
src/connection_cache.rs): Manages connection pooling for performance optimization - Stats (
src/stats.rs): Per-connection and aggregate statistics with UDP reporting - Manager (
src/manager.rs): Central manager dashboard for monitoring all proxy instances - Main (
src/main.rs): Command-line interface and application startup
When multiple targets are specified (comma-separated), rustproxy operates in load balancing mode, distributing incoming TCP connections across the backends.
| Algorithm | Flag | Description |
|---|---|---|
| Round Robin | --lb roundrobin |
Cycles through enabled backends sequentially |
| Random | --lb random |
Selects a random enabled backend for each connection |
If --lb is not specified but multiple targets are given, round-robin is used by default.
When --http-interface is specified, a built-in web dashboard is available with:
- Summary header: Listen address, algorithm, uptime, total active connections, aggregate TX/RX, throughput rates
- Traffic history graph: Real-time visualization of TX/RX rates with historical data
- Backend table: ID, address, status (UP/PING FAIL/DISABLED), active connections, total connections, TX, RX, errors, HTTP ping latency
- Enable/Disable buttons: Toggle backends on/off at runtime (admin-controlled disable persists through health checks)
- SS Clients section: View active Shadowsocks client connections with IP addresses and traffic stats
- SS mode badge: Shows cipher method when running in SS+TCP LB mode
- Auto-refresh: Polls
/api/statsand/api/connectionsevery 2 seconds - Color coding: Green = enabled, Red = disabled/failed, Yellow = has errors
- 24-hour traffic tracking: Persistent traffic statistics across restarts
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Web dashboard |
/api/backends |
GET | JSON list of all backends with algorithm |
/api/backends/:id/enable |
POST | Enable a backend |
/api/backends/:id/disable |
POST | Disable a backend |
/api/stats |
GET | Full stats (uptime, connections, bytes, all backends) |
/api/connections |
GET | Active and recent per-connection stats |
/api/health |
GET | Health check |
Example API usage:
# List backends
curl http://localhost:8888/api/backends
# Disable backend 1
curl -X POST http://localhost:8888/api/backends/1/disable
# Re-enable backend 1
curl -X POST http://localhost:8888/api/backends/1/enable
# Get full stats
curl http://localhost:8888/api/statsWhen --healthcheck is enabled (TCP LB mode only), rustproxy continuously monitors backend health:
- Probe method: Direct HTTP GET to each backend, verifies
HTTP/response - Interval: Every 60 seconds (5-second initial delay after startup)
- Timeout: 10 seconds per probe
- On success: Backend stays enabled (or is re-enabled if previously disabled); response time is recorded
- On failure/timeout: Backend is disabled and removed from rotation
- Admin override: Backends manually disabled via API/dashboard stay disabled even if health checks pass
- Safety valve: If ALL backends fail, all are re-enabled to avoid total outage
- Dashboard integration: Healthcheck status and response times are visible in the web dashboard
On startup in LB mode, rustproxy performs a non-blocking self-test:
- Tries connecting to the proxy listener (5s timeout)
- Tries connecting to each backend (3s timeout)
- Logs PASS/WARN for each — warnings only, does not block startup
- Protocol Compliance: Full SOCKS5 protocol implementation (RFC 1928)
- Authentication Methods:
- No authentication (anonymous access)
- Username/password authentication (RFC 1929)
- Connection Types: CONNECT command support (most common use case)
- Address Types: IPv4, IPv6, and domain name resolution
- Connection Caching: Reuse connections for improved performance
- Error Handling: Proper SOCKS5 error responses for various failure conditions
To use the SOCKS5 proxy with various applications:
cURL:
curl --socks5 127.0.0.1:1080 https://example.com
curl --socks5-hostname 127.0.0.1:1080 https://example.com # DNS through proxySSH:
ssh -o ProxyCommand='nc -X 5 -x 127.0.0.1:1080 %h %p' user@target.comFirefox:
- Go to Settings → Network Settings → Manual proxy configuration
- Set SOCKS Host: 127.0.0.1, Port: 1080, SOCKS v5
Environment Variables:
export ALL_PROXY=socks5://127.0.0.1:1080
export all_proxy=socks5://127.0.0.1:1080Quick validation:
./test_simple.shUnit tests only:
cargo testComprehensive integration tests:
./test_all.shcargo benchMeasured on Apple M4 (macOS), loopback, release build (2026-07):
| Scenario | Tool | Result |
|---|---|---|
TCP relay throughput (--mode tcp) |
iperf3 -t 5 |
86.3 Gbit/s (direct loopback baseline: 140 Gbit/s) |
HTTP forward proxy, plain HTTP (--mode http) |
hey -n 50000 -c 100 |
56,000 req/s, avg 1.7 ms, p99 4.6 ms |
| HTTPS via CONNECT tunnel | hey -n 50000 -c 100 |
~1,700 req/s (bounded by the single-threaded Node.js TLS test backend), 100% 2xx |
| CONNECT tunnel bulk download (200 MB) | curl |
438 MB/s (~3.5 Gbit/s, bounded by the Node.js TLS test backend) |
Reproduce: run a local backend (node -e 'require("http").createServer((q,s)=>s.end("ok")).listen(9000)'), start rustproxy --listen 127.0.0.1:18080 --mode http, then hey -n 50000 -c 100 -x http://127.0.0.1:18080 http://127.0.0.1:9000/. For TCP mode, point --target at a local iperf3 -s and run iperf3 -c against the proxy port.
Same machine, same backend and load; Squid configured with caching and access logging disabled:
| Metric | rustproxy | Squid 6.13 |
|---|---|---|
Plain HTTP forward proxy (hey -c 100) |
56,244 req/s | 10,929 req/s |
HTTPS CONNECT rate (hey -c 100, 3 alternating rounds) |
760–1,740 req/s | 1,100–2,830 req/s (both bounded by the TLS test backend; high variance) |
| CONNECT bulk throughput (200 MB, 3 alternating rounds) | 359–494 MB/s | 318–473 MB/s (tie — both bounded by the TLS test backend) |
| Raw TCP relay | 86 Gbit/s | n/a (no raw TCP mode) |
| Memory (RSS, light load) | ~9 MB | ~27 MB |
rustproxy keeps a persistent backend connection pool (hyper, 90s idle timeout, TCP_NODELAY both sides), which puts plain-HTTP forwarding ~5× ahead of Squid. CONNECT-heavy workloads (typical HTTPS browsing) are equivalent within measurement noise — both saturate the test backend. rustproxy additionally offers built-in DoT/DoH DNS, SOCKS5/Shadowsocks modes, TCP load balancing, and a ~3× smaller footprint in a single static binary.
- tokio: Async runtime
- axum: Web framework (dashboard + REST API)
- hyper: HTTP client/server library
- shadowsocks: Shadowsocks protocol (AEAD cipher decryption, proxy listener)
- rand: Random backend selection
- tracing: Structured logging
- serde/serde_json: JSON serialization
- bytes: Byte buffer utilities
- futures-util: Future utilities
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.