VLESS with Reality is currently one of the strongest setups for bypassing network censorship. Unlike traditional VPNs that have detectable signatures, Reality makes your traffic indistinguishable from normal HTTPS connections to major websites.

This guide walks through the complete setup process: from server installation to client configuration, multi-user management, and traffic monitoring.

Why VLESS + Reality?#

Reality solves a fundamental problem with proxy detection. Traditional approaches use synthetic TLS certificates that deep packet inspection (DPI) systems can fingerprint. Reality instead “borrows” the TLS identity of real websites like Microsoft or Cloudflare.

Key advantages:

  • Traffic looks identical to legitimate HTTPS connections
  • Uses actual TLS fingerprints from major websites
  • No domain or certificate management required
  • TCP-based (avoids UDP throttling)
  • Fast and stable for daily use

When a censor inspects your connection, they see what appears to be a normal visit to microsoft.com. Only clients with the correct cryptographic keys can actually connect to your server.

Prerequisites#

Before starting, you’ll need:

  • A VPS with root access (Hetzner, DigitalOcean, or similar)
  • Ubuntu 22.04 LTS (recommended)
  • SSH access to your server
  • A client app (Shadowrocket, v2rayN, or similar)

Recommended server specifications:

  • 2+ CPU cores
  • 2+ GB RAM
  • Located in a privacy-friendly region (Finland, Netherlands, Germany)

Initial Server Setup#

Connect to your server and update the system:

ssh root@your_server_ip
apt update && apt upgrade -y

Running services as root is a security risk. Create a dedicated user:

adduser xray-admin
usermod -aG sudo xray-admin

Set up SSH key authentication for the new user:

mkdir -p /home/xray-admin/.ssh
cp ~/.ssh/authorized_keys /home/xray-admin/.ssh/
chown -R xray-admin:xray-admin /home/xray-admin/.ssh
chmod 700 /home/xray-admin/.ssh
chmod 600 /home/xray-admin/.ssh/authorized_keys

Installing Xray-core#

Install Xray using the official script:

sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install

This creates:

  • Binary at /usr/local/bin/xray
  • Config directory at /usr/local/etc/xray/
  • Systemd service xray.service

Verify the installation:

xray version

Generating Keys#

Reality requires three types of credentials: a key pair, a UUID, and short IDs.

Generate the Key Pair#

xray x25519

Output:

Private key: eLJwm7VGlO4pVxxxxxxxxxxxxxQ8C_uMVxxxxxxxx
Public key: nO8BvFqxxxxxxxxxxxxx-2MxxxxxxxxxxxxxY

Save both keys securely. The private key stays on the server; the public key goes to clients.

Generate a UUID#

xray uuid

This creates a unique identifier for client authentication.

Generate Short IDs#

openssl rand -hex 8

Short IDs act as additional authentication tokens. Generate multiple for different users or devices.

Server Configuration#

Create the Xray configuration file:

sudo nano /usr/local/etc/xray/config.json

Paste the following configuration:

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "YOUR-UUID-HERE",
            "flow": "xtls-rprx-vision",
            "email": "[email protected]"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "show": false,
          "dest": "www.microsoft.com:443",
          "xver": 0,
          "serverNames": [
            "www.microsoft.com"
          ],
          "privateKey": "YOUR-PRIVATE-KEY-HERE",
          "shortIds": [
            "YOUR-SHORT-ID-HERE"
          ]
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    }
  ]
}

Replace the placeholder values with your generated credentials.

Configuration Explained#

inbounds: How connections enter your server

  • port: 443 — Standard HTTPS port, blends with normal traffic
  • protocol: "vless" — Lightweight, modern proxy protocol
  • flow: "xtls-rprx-vision" — Reality-specific flow control

realitySettings: The stealth layer

  • dest — Website whose TLS fingerprint to borrow
  • serverNames — Domains clients claim to visit
  • privateKey — Your server’s secret key
  • shortIds — Authentication tokens

outbounds: Where traffic exits

  • protocol: "freedom" — Direct connection to the internet

Firewall Configuration#

Configure UFW to allow only essential ports:

# Allow SSH first (critical!)
sudo ufw allow 22/tcp

# Allow Xray
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable

# Verify rules
sudo ufw status verbose

Starting the Service#

Fix Port Binding Permissions#

Port 443 is privileged. Grant Xray permission to use it:

sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/xray

Enable and Start Xray#

sudo systemctl enable xray
sudo systemctl start xray
sudo systemctl status xray

Verify the port is listening:

sudo ss -tulpn | grep 443

Expected output:

tcp   LISTEN   0   128   *:443   *:*   users:(("xray",pid=1234,fd=3))

Test Configuration Syntax#

Before restarting after config changes:

sudo xray run -test -config /usr/local/etc/xray/config.json

Performance Optimization#

Enable BBR congestion control for better throughput:

echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Verify BBR is active:

sysctl net.ipv4.tcp_congestion_control

BBR significantly improves speed on high-latency or lossy networks.

Client Configuration#

Each client needs these parameters:

Parameter Value
Address Your server IP
Port 443
UUID Your generated UUID
Flow xtls-rprx-vision
Security reality
SNI www.microsoft.com
Fingerprint chrome
Public Key Your public key
Short ID Your short ID
Network tcp
  • Windows: v2rayN, Nekoray
  • macOS: V2RayXS, FoXray
  • Android: v2rayNG, NekoBox
  • iOS: Shadowrocket, FoXray
  • Linux: Nekoray, v2rayA

Shadowrocket Configuration (iOS)#

  1. Tap + to add a new server
  2. Select TypeVLESS
  3. Fill in Address, Port, UUID
  4. Enable TLS
  5. Set Security to reality
  6. Enter SNI, Public Key, Short ID
  7. Set XTLS to xtls-rprx-vision
  8. Save and connect

Testing the Connection#

After connecting, verify your IP has changed:

https://ifconfig.me

The displayed IP should match your server’s IP.

Adding Multiple Users#

Each user needs a unique UUID. They share the same server settings.

Generate Additional UUIDs#

xray uuid

Update Configuration#

Edit the clients array:

"clients": [
  {
    "id": "UUID-FOR-USER-1",
    "flow": "xtls-rprx-vision",
    "email": "[email protected]"
  },
  {
    "id": "UUID-FOR-USER-2",
    "flow": "xtls-rprx-vision",
    "email": "[email protected]"
  }
]

The email field is optional but helps identify users in logs.

Restart Xray after changes:

sudo systemctl restart xray

Traffic Monitoring#

Enable statistics to track per-user traffic.

Update Configuration for Stats#

Add these sections to your config:

{
  "stats": {},
  "api": {
    "tag": "api",
    "services": ["StatsService"]
  },
  "policy": {
    "levels": {
      "0": {
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true
    }
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 10085,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      },
      "tag": "api"
    }
  ],
  "routing": {
    "rules": [
      {
        "inboundTag": ["api"],
        "outboundTag": "api",
        "type": "field"
      }
    ]
  }
}

Query Traffic Statistics#

xray api statsquery --server=127.0.0.1:10085

Query specific user:

xray api statsquery --server=127.0.0.1:10085 \
  --pattern "user>>>[email protected]>>>traffic>>>downlink"

Traffic Monitoring Script#

Create a script to display formatted statistics:

#!/bin/bash

# Add color output
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

echo -e "${BLUE}=== Xray Traffic Statistics ===${NC}"
echo ""
echo "Timestamp: $(date)"
echo "Server uptime: $(uptime -p)"
echo ""

# Check if Xray is running
if ! systemctl is-active --quiet xray; then
    echo -e "${RED}❌ Xray service is not running!${NC}"
    exit 1
fi

# Function to convert bytes to human readable
bytes_to_human() {
    local bytes=$1
    if [ -z "$bytes" ] || [ "$bytes" = "0" ]; then
        echo "0 B"
    else
        numfmt --to=iec-i --suffix=B "$bytes" 2>/dev/null || echo "$bytes bytes"
    fi
}

# Get all user stats
stats_output=$(xray api statsquery --server=127.0.0.1:10085 2>/dev/null)

if [ $? -ne 0 ]; then
    echo "❌ Error: Cannot connect to Xray API"
    echo "Make sure Xray is running: sudo systemctl status xray"
    exit 1
fi

# Extract unique user emails
emails=$(echo "$stats_output" | grep -oP 'user>>>.*?>>>traffic' | cut -d'>' -f4 | sort -u)

if [ -z "$emails" ]; then
    echo "⚠️  No users found. This could mean:"
    echo "   - No traffic has been generated yet"
    echo "   - API is not configured correctly"
    exit 0
fi

# Loop through each user
for email in $emails; do
    # Get uplink
    uplink=$(xray api statsquery --server=127.0.0.1:10085 --pattern "user>>>${email}>>>traffic>>>uplink" 2>/dev/null | grep -oP '"value":\s*\K[0-9]+' || echo "0")

    # Get downlink
    downlink=$(xray api statsquery --server=127.0.0.1:10085 --pattern "user>>>${email}>>>traffic>>>downlink" 2>/dev/null | grep -oP '"value":\s*\K[0-9]+' || echo "0")

    # Calculate total
    total=$((uplink + downlink))

    echo -e "${GREEN}🌐 User: $email${NC}"
    echo "   ⬆️  Upload:   $(bytes_to_human $uplink)"
    echo "   ⬇️  Download: $(bytes_to_human $downlink)"
    echo "   📊 Total:    $(bytes_to_human $total)"
    echo ""
done

echo "---"
echo "💡 Tip: Run 'sudo systemctl restart xray' to reset counters"

Security Enhancements#

Multiple Server Names#

Using multiple serverNames makes your traffic pattern more diverse:

"serverNames": [
  "www.microsoft.com",
  "www.cloudflare.com",
  "addons.mozilla.org"
]

Different clients can use different SNIs, making pattern detection harder.

Good choices for serverNames:

  • CDN providers: www.cloudflare.com
  • Major tech companies: www.microsoft.com, www.apple.com
  • Popular services: addons.mozilla.org

Multiple Short IDs#

Assign different short IDs to different users or devices:

"shortIds": [
  "a1b2c3d4e5f6g7h8",
  "9876543210abcdef",
  "028f3f4a7b6c9d8e"
]

Benefits:

  • Revoke individual access without affecting others
  • Track which device/user connected
  • Identify leaks by seeing which ID was compromised

Key Rotation#

Every 3-6 months:

  1. Generate new short IDs
  2. Update server configuration
  3. Distribute new credentials to users
  4. Remove old short IDs after a grace period

Troubleshooting#

Common Issues#

Connection timeout

  • Check if Xray is running: sudo systemctl status xray
  • Verify port is listening: sudo ss -tulpn | grep 443
  • Check firewall rules: sudo ufw status

Permission denied on port 443

  • Apply capability: sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/xray
  • Restart service: sudo systemctl restart xray

Configuration errors

  • Test syntax: sudo xray run -test -config /usr/local/etc/xray/config.json
  • Check logs: sudo journalctl -u xray -n 50

Useful Commands#

# Check service status
sudo systemctl status xray

# View recent logs
sudo journalctl -u xray -n 100

# Live log monitoring
sudo journalctl -u xray -f

# Restart after config changes
sudo systemctl restart xray

# Test configuration syntax
sudo xray run -test -config /usr/local/etc/xray/config.json

Keeping Updated#

Update Xray periodically for security patches:

sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
sudo systemctl restart xray

Back up your configuration before updates:

sudo cp /usr/local/etc/xray/config.json ~/xray-config-backup.json

Summary#

VLESS with Reality provides strong censorship resistance by making proxy traffic indistinguishable from normal HTTPS. The setup requires careful attention to key generation and configuration, but once running, it’s stable and fast.

Key points to remember:

  • Keep your private key and UUIDs secure
  • Use multiple serverNames and shortIds for better security
  • Monitor traffic to detect unusual patterns
  • Update Xray regularly for security fixes
  • Always test configuration changes before restarting