Why Do Regular VPNs Get Blocked So Easily?
I manage the network for a 50-person office and a small datacenter, so dealing with VPN blocks is a familiar headache. OpenVPN, WireGuard — both are solid on security, but they share a common weakness: their traffic has a recognizable fingerprint. Deep Packet Inspection (DPI) can identify and block them within seconds.
Xray-core with XTLS-Reality takes a completely different approach. Instead of wrapping traffic in a VPN tunnel and sending it out, it makes the traffic look exactly like regular HTTPS to a well-known website. When a firewall inspects it, it sees a valid TLS handshake with a familiar domain name — nothing suspicious. This technique is sometimes called “traffic camouflage.”
How Is XTLS-Reality Different from a Regular VPN?
- OpenVPN/WireGuard: Create a separate tunnel with distinctive headers that are easy to detect via DPI
- XTLS-Reality: “Borrows” the TLS certificate of a real website (e.g., www.microsoft.com) — traffic looks identical to HTTPS going to that domain
- No need for your own domain or SSL certificate — the server works without them
- Uses the VLESS protocol — lighter than VMess, with no unnecessary overhead
Prerequisites
Before getting started, make sure you have the following ready:
- A Linux VPS (Ubuntu 20.04+ or Debian 11+) located outside the restricted network
- SSH access to the server with root or sudo privileges
- Port 443 not already occupied by nginx or apache
This guide was written for Ubuntu 22.04. If port 443 is already in use, either move Xray to port 8443 or set up nginx as a reverse proxy — I cover that in the troubleshooting section at the end.
Installing Xray-core on a Linux Server
Step 1: Install Xray-core
Xray-core has an official install script — one command and you’re done:
# Install the latest version of Xray-core
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
# Check the version
xray version
The installer handles everything else automatically:
- Downloads the binary to
/usr/local/bin/xray - Creates a systemd service
xray.service - Creates the config directory at
/usr/local/etc/xray/
Step 2: Generate a UUID and Keypair for XTLS-Reality
What sets Reality apart is its use of an X25519 keypair instead of a traditional SSL certificate. No domain registration needed, no Let’s Encrypt — far simpler than the conventional approach.
# Generate a UUID (used as the "password" for clients)
xray uuid
# Example output: a3f2b1c4-d5e6-7890-abcd-ef1234567890
# Generate a keypair for Reality
xray x25519
# Output:
# Private key: aBcDeFgH...
# Public key: XyZwVuTs...
Save all three values right now: the UUID, Private key, and Public key. The private key is especially sensitive — anyone who obtains it can impersonate your server.
Step 3: Generate a Short ID
# Generate a random short ID (8 bytes hex)
openssl rand -hex 8
# Example: a1b2c3d4e5f6a7b8
Configuring Xray-core with XTLS-Reality
Server-Side Configuration
Create the file /usr/local/etc/xray/config.json:
{
"log": {
"loglevel": "warning",
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log"
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "REPLACE-WITH-YOUR-UUID",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "www.microsoft.com:443",
"xver": 0,
"serverNames": [
"www.microsoft.com"
],
"privateKey": "REPLACE-WITH-PRIVATE-KEY",
"shortIds": [
"REPLACE-WITH-SHORT-ID"
]
}
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
]
}
The three most important parameters to understand:
dest: "www.microsoft.com:443"— traffic appears to be connecting to Microsoft. Can be replaced with any major domain that supports TLS 1.3flow: "xtls-rprx-vision"— the latest XTLS mode, currently the most effective at bypassing DPIprivateKey— enter the Private key (not the Public key!) from thexray x25519command
Starting the Service
# Create the log directory
mkdir -p /var/log/xray
chown nobody:nogroup /var/log/xray
# Validate the config before starting
xray run -test -config /usr/local/etc/xray/config.json
# Start the service and enable autostart
systemctl start xray
systemctl enable xray
# Check the service status
systemctl status xray
# Open the port in UFW (Ubuntu)
ufw allow 443/tcp
ufw reload
# If using iptables
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables-save > /etc/iptables/rules.v4
Client Configuration
Import via Connection String (Fastest Method)
The quickest way is to generate a URI and paste it directly into your app. v2rayN (Windows), v2rayNG (Android), and Shadowrocket (iOS) all support this format:
vless://UUID@SERVER-IP:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.microsoft.com&fp=chrome&pbk=PUBLIC-KEY&sid=SHORT-ID&type=tcp#MyXrayVPN
Replace each placeholder accordingly:
UUID→ the UUID generated in Step 2SERVER-IP→ your VPS IP addressPUBLIC-KEY→ the Public key (not the Private key)SHORT-ID→ the Short ID from Step 3
Manual Configuration on a Linux Client
On a Linux client, install Xray the same way as on the server, then create a separate client config. The local Xray instance will run as a SOCKS5 proxy on port 1080, and other apps route their traffic through it:
{
"inbounds": [
{
"port": 1080,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": { "udp": true }
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "SERVER-IP",
"port": 443,
"users": [
{
"id": "UUID",
"flow": "xtls-rprx-vision",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "www.microsoft.com",
"fingerprint": "chrome",
"show": false,
"publicKey": "PUBLIC-KEY",
"shortId": "SHORT-ID",
"spiderX": "/"
}
}
}
]
}
# Run the Xray client
xray run -config client-config.json
# Test the connection through the SOCKS5 proxy
curl --proxy socks5h://127.0.0.1:1080 https://api.ipify.org
# Output will be your VPS IP — confirms traffic is routing through the proxy
Testing and Monitoring
Viewing Logs and Server Status
# Stream logs in real time
journalctl -u xray -f
# View the access log
tail -f /var/log/xray/access.log
# View the error log
tail -f /var/log/xray/error.log
# Check which ports are listening
ss -tlnp | grep :443
Quick Monitoring Script
This is the script I use to check Xray every morning — takes 2 seconds to know whether the server is healthy:
#!/bin/bash
# Save to /usr/local/bin/xray-check.sh and chmod +x
echo "=== Xray Service ==="
systemctl is-active xray && echo "Status: RUNNING" || echo "Status: STOPPED"
echo ""
echo "=== Active Connections ==="
ss -tnp | grep xray | wc -l | xargs -I{} echo "Connections: {}"
echo ""
echo "=== Last 5 Errors ==="
tail -5 /var/log/xray/error.log 2>/dev/null || echo "No errors"
echo ""
echo "=== Memory Usage ==="
ps aux | grep '[x]ray' | awk '{print "CPU: "$3"% | MEM: "$4"% | PID: "$2}'
chmod +x /usr/local/bin/xray-check.sh
xray-check.sh
Updating Xray-core When a New Version Is Released
# Re-run the installer to update
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
# Confirm the new version
xray version
# Restart the service
systemctl restart xray
Common Errors and How to Fix Them
Error “failed to handler mux client connection”: The client and server are using different flow settings. Make sure both are set to xtls-rprx-vision.
Port 443 is occupied by nginx/apache: Move Xray to port 8443 in the config, or configure nginx stream proxy to forward traffic to Xray based on SNI.
Traffic is still being blocked: Try changing dest to a different domain with strong TLS 1.3 support — addons.mozilla.org:443 or www.cloudflare.com:443 usually work well. Remember to update serverNames to match.
Adding a new user: Generate a new UUID (xray uuid), add it to the clients array in the server config, then run systemctl reload xray. Each person gets their own UUID — makes access management clean and simple.

