Skip to content

SOCKS5 Proxy Mode

SOCKS5 mode is the default and most flexible way to use r-vpn. It runs a local SOCKS5 proxy that individual apps can be pointed at — without affecting other traffic on your machine.


Starting the Proxy

rvpn -c ~/.config/rvpn/client.toml

Default listen address: 127.0.0.1:1080

Once running you'll see:

INFO  SOCKS5 proxy listening on 127.0.0.1:1080


Configuring Apps

macOS — System-wide Proxy

System Settings → Network → your connection → Details → Proxies

Enable SOCKS Proxy and set: - Server: 127.0.0.1 - Port: 1080

This routes all system traffic (Safari, curl, etc.) through the VPN.

Firefox

Settings → General → Network Settings → Manual proxy configuration

  • SOCKS Host: 127.0.0.1
  • Port: 1080
  • Select SOCKS v5
  • Check Proxy DNS when using SOCKS v5 to prevent DNS leaks (not needed if you have the DNS proxy enabled)

Chrome / Brave

Chrome uses the system proxy on macOS. On Linux, use the SwitchyOmega extension:

  1. Install SwitchyOmega
  2. Create a new profile → Protocol: SOCKS5, Server: 127.0.0.1, Port: 1080
  3. Switch to that profile when you want to use the VPN

curl

curl --socks5 127.0.0.1:1080 https://api.ipify.org

Linux — System-wide (environment variables)

export ALL_PROXY=socks5://127.0.0.1:1080
export HTTPS_PROXY=socks5://127.0.0.1:1080
export HTTP_PROXY=socks5://127.0.0.1:1080

Add to ~/.bashrc or ~/.zshrc to persist across sessions.


HTTP Proxy

r-vpn can also run as an HTTP proxy alongside the SOCKS5 proxy. This is useful when apps or environments expect HTTP_PROXY/HTTPS_PROXY environment variables pointing at an HTTP proxy (the most common convention for CLI tools like curl, git, npm, pip, and Docker).

Both proxies share the same WebSocket connection pool — running both simultaneously adds zero overhead.

Enable the HTTP Proxy

[http_proxy]
enabled        = true
listen_address = "127.0.0.1:8118"

Restart the client — you should see:

INFO  HTTP proxy listening on 127.0.0.1:8118
INFO  SOCKS5 proxy listening on 127.0.0.1:1080

Using Environment Variables

The most common way to use the HTTP proxy is via environment variables. Most CLI tools and programming languages respect these automatically:

export HTTP_PROXY=http://127.0.0.1:8118
export HTTPS_PROXY=http://127.0.0.1:8118
export ALL_PROXY=http://127.0.0.1:8118

After setting these, tools like curl, git, npm, pip, wget, and Docker route through the VPN without any per-app configuration:

# No --proxy flag needed — env vars are picked up automatically
curl https://api.ipify.org
git clone https://github.com/user/repo.git
pip install requests

Add to ~/.bashrc or ~/.zshrc to persist across sessions.

curl (explicit)

curl -x http://127.0.0.1:8118 https://api.ipify.org

How It Works

The HTTP proxy handles two request types:

  • HTTP CONNECT — used for HTTPS. The client sends CONNECT host:443 HTTP/1.1, the proxy responds with 200 Connection Established, and traffic flows through an encrypted tunnel to the destination.
  • Plain HTTP forwarding — used for unencrypted HTTP. The client sends GET http://host/path HTTP/1.1, the proxy connects to the host and forwards the request.

Both paths are split-tunnel-aware and use the same connection pool as SOCKS5.

Authentication

To require Basic authentication:

[http_proxy]
enabled        = true
listen_address = "127.0.0.1:8118"
auth_enabled   = true
auth_username  = "user"
auth_password  = "changeme"

Clients must send Proxy-Authorization: Basic ... header (browsers and curl -x handle this automatically when credentials are in the URL: http://user:pass@127.0.0.1:8118).

SOCKS5 vs HTTP Proxy

SOCKS5 HTTP Proxy
Protocols Any (TCP + UDP) HTTP and HTTPS only
Environment variables ALL_PROXY=socks5://... HTTP_PROXY=http://...
Browser support Native on macOS/Firefox Universal (all browsers)
CLI tool support Some tools (curl, Node) Most tools (curl, git, pip, npm, Docker)
Authentication SOCKS5 username/password HTTP Basic auth
Best for Per-app routing, UDP System-wide env vars, CI/CD

In most cases, use the HTTP proxy with environment variables — it has the broadest compatibility with CLI tools and build systems. Use SOCKS5 for per-app routing or when you need UDP support.


Split Tunneling

Split tunneling lets you route only certain traffic through the VPN while the rest connects directly. This is useful when you want to reach blocked sites through the VPN while keeping local network and domestic traffic unaffected.

Built-in China bypass

Enable split tunneling with automatic China IP bypass in client.toml:

[split_tunnel]
enabled = true
builtin_bypass_countries = ["CN"]

When enabled, traffic to Chinese IPs (based on APNIC data — ~8800 networks) connects directly, and everything else routes through the VPN.

Custom bypass networks

[split_tunnel]
enabled = true
bypass_networks_file = "~/.config/rvpn/bypass-networks.txt"

bypass-networks.txt — one CIDR per line:

192.168.0.0/16
10.0.0.0/8
172.16.0.0/12

Ad blocking

[split_tunnel]
block_ads = true

Blocks known ad and tracker domains at the DNS level. No bytes sent, no connection established.


DNS Proxy

Important: Without the DNS proxy, your DNS queries may leak to your ISP even when using the SOCKS5 proxy. See DNS Leak Prevention for a complete explanation.

By default, DNS queries are resolved by your system's DNS server — outside the VPN tunnel. This means your ISP can observe which domains you look up even while your traffic is proxied.

r-vpn includes a built-in DNS proxy that resolves all queries server-side through the same encrypted WebSocket tunnel. It is also split-tunnel aware: bypass domains are resolved locally, and blocked ad/tracker domains return NXDOMAIN immediately without ever hitting the network.

Enable the DNS proxy

[dns_proxy]
enabled        = true
listen_address = "127.0.0.1:53"

Note: Port 53 requires root or CAP_NET_BIND_SERVICE. Use port 5353 for unprivileged testing (see below).

Restart the client — you should see:

INFO  DNS proxy listening on 127.0.0.1:53

macOS — system-wide

Run the client with sudo (so it can bind port 53), then add 127.0.0.1 as your DNS server:

System Settings → Network → your connection → Details → DNS → +127.0.0.1

Or via the command line (replace Wi-Fi with your interface name):

sudo networksetup -setdnsservers Wi-Fi 127.0.0.1

To restore the original DNS when you're done:

sudo networksetup -setdnsservers Wi-Fi empty

Linux — system-wide

Run the client as root (or with the CAP_NET_BIND_SERVICE capability) with listen_address = "127.0.0.1:53", then point your resolver at it.

/etc/resolv.conf (direct):

nameserver 127.0.0.1

systemd-resolved — add to /etc/systemd/resolved.conf:

[Resolve]
DNS=127.0.0.1
Then restart: sudo systemctl restart systemd-resolved

Unprivileged testing (port 5353)

[dns_proxy]
enabled        = true
listen_address = "127.0.0.1:5353"

Verify it works:

dig @127.0.0.1 -p 5353 example.com

The response will come from your VPN server rather than your local ISP.


Connection Modes

The SOCKS5 proxy supports two connection modes to the server:

Each SOCKS5 flow opens its own separate WebSocket connection with its own X3DH handshake and DoubleRatchet. This is the recommended mode.

Why this is the default: - Traffic pattern matches standard tools — each connection is an independent WebSocket, similar to normal HTTPS browsing - Short-lived connections are harder for traffic classifiers to identify and block - Simpler failure isolation — one connection dropping doesn't affect others

[socks5]
multiplex = false       # default

Multiplexed Mode (Optional)

All SOCKS5 flows share a single WebSocket connection with one shared DoubleRatchet session. Each SOCKS5 CONNECT creates a logical "flow" via CreateFlow/CloseFlow control messages over the same tunnel.

Benefits: - Single TLS handshake, single X3DH key exchange — ~250ms overhead vs ~600ms per-connection - 0-RTT flow creation — data is sent immediately without waiting for server ACK - Lower server resource usage (one WebSocket, one ratchet vs one per flow) - Server supports up to 2000 concurrent flows per mux session - 100% success rate in stress tests (vs ~85% for standard mode)

Trade-offs: - A single long-lived binary stream is a distinctive traffic pattern that classifiers can detect - All flows share one connection — if it drops, everything reconnects

How it works: 1. First SOCKS5 CONNECT opens the mux WebSocket tunnel ({server_path}/mux) 2. Performs X3DH handshake to establish the shared DoubleRatchet 3. Subsequent flows send CreateFlow control messages over the same tunnel 4. Client sends data immediately (0-RTT) — server buffers while connecting to target 5. Data flows through multiplexed frames

Security: The 0-RTT optimization is NOT TLS 0-RTT. The TLS handshake and X3DH key exchange are complete before any data flows. Replay protection is provided by the Double Ratchet — each message has a unique message_number, and the ratchet rejects messages with number < current (see ratchet.rs:decrypt). Message keys are consumed after use, so replays fail with "Message too old".

Configuration:

[socks5]
multiplex = true
# mux_path defaults to {server_path}/mux automatically

To override the mux endpoint explicitly:

[socks5]
multiplex = true
mux_path = "/api/v1/ws/mux"

Comparing Modes

Standard Multiplexed
WebSockets per session 1 per flow 1
X3DH handshakes 1 per flow 1
Server flows Unlimited Up to 2000
Server-side WS connections N 1
Traffic pattern Normal HTTPS-like Single long-lived stream
Success rate (HK, 15min) ~85% 100%
Latency p50 (HK) ~575ms ~315ms
Latency p95 (HK) ~2000ms ~1060ms
Best for DPI evasion, traffic blending Reliability, high throughput

Note: Standard mode opens one WebSocket per TCP flow. On busy networks (e.g. browsers with many tabs), this can exhaust the server's max_connections_per_ip limit. If needed, increase the server's rate limits:

[server.rate_limit]
max_connections_per_ip    = 500
max_handshakes_per_minute = 2000


Changing the Listen Address

To listen on a specific interface (e.g. to share the proxy with other devices on your local network):

[socks5]
listen_address = "0.0.0.0:1080"

Security note: Only expose the SOCKS5 port on trusted networks. There is no authentication by default.

To add authentication:

[socks5]
listen_address = "0.0.0.0:1080"
auth_enabled   = true
auth_username  = "user"
auth_password  = "changeme"

Running as a Service

Linux (systemd)

sudo nano /etc/systemd/system/rvpn-client.service
[Unit]
Description=r-vpn Client
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=YOUR_USER
ExecStart=/usr/local/bin/rvpn -c /etc/rvpn/client.toml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now rvpn-client

FreeBSD (rc.d)

Create /usr/local/etc/rc.d/rvpn_client:

#!/bin/sh
# PROVIDE: rvpn_client
# REQUIRE: NETWORKING
# KEYWORD: shutdown

. /etc/rc.subr

name="rvpn_client"
rcvar="rvpn_client_enable"
command="/usr/local/bin/rvpn"
command_args="-c /usr/local/etc/rvpn/client.toml"
pidfile="/var/run/rvpn-client.pid"

load_rc_config $name
run_rc_command "$1"
chmod +x /usr/local/etc/rc.d/rvpn_client
echo 'rvpn_client_enable="YES"' >> /etc/rc.conf
service rvpn_client start