Files
common-skills/skills/xui-deploy/references/xray-inbound-config.md
T
Team 881fbf6dbb fix: xui inbound client must have enable:true, fix WS nginx headers
- lessons-learned: add two critical issues from first real deployment
  1. client enable:false causes auto-removal by x-ui scheduler
  2. CF proxy strips Connection header, nginx must hardcode WS headers
- xray-inbound-config.md: fix API path, add enable:true to client,
  hardcode Upgrade/Connection headers in nginx WS location
2026-04-25 20:20:03 +08:00

5.3 KiB

Recommended Xray Inbound Configuration

This reference covers the best protocol/transport combinations for security and performance, and the API payloads to create them via the x-ui REST API.

Protocol Recommendation (ranked)

Rank Protocol Transport TLS Why
Best VLESS WebSocket TLS (via Nginx) CDN-friendly, low overhead, widely supported
Best VLESS gRPC TLS (via Nginx) Multiplexed, low latency, CDN-friendly
Good VLESS TCP XTLS/Reality No CDN needed, excellent performance, anti-detection
Good VMess WebSocket TLS (via Nginx) Broad client support
Avoid VMess TCP none Detectable, no forward secrecy
Avoid Shadowsocks Blocked in many regions

Default recommendation: VLESS + WebSocket + TLS, proxied through Nginx.

  • Panel port stays on 127.0.0.1 (localhost-only)
  • Nginx terminates TLS on 443 and forwards to the inbound port on 127.0.0.1
  • Inbound port also stays localhost-only (e.g. 127.0.0.1:10000)

Step-by-Step: Create VLESS + WS + TLS Inbound

1. Collect inbound parameters

Ask the user (or use defaults):

Field Default
Inbound port 10000
WS path /ws/ (use a random string for security)
Remark vless-ws

2. Create inbound via x-ui API

The x-ui panel exposes a REST API at http://127.0.0.1:<panel_port><base_path>. Authenticate with a session cookie first, then POST the inbound.

# 1. Login and save session cookie
PANEL_USER=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" UserName)
PANEL_PASS=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" Password)

ssh -i <key_path> -p <ssh_port> <user>@<host> "
  curl -sc /tmp/xui-cookie.txt \
    -X POST http://127.0.0.1:<panel_port><base_path>login \
    -H 'Content-Type: application/json' \
    -d '{\"username\":\"'\"$PANEL_USER\"'\",\"password\":\"'\"$PANEL_PASS\"'\"}'
"

# 2. Generate a UUID for the client
UUID=$(ssh -i <key_path> -p <ssh_port> <user>@<host> "cat /proc/sys/kernel/random/uuid")

# 3. Create VLESS + WebSocket inbound
ssh -i <key_path> -p <ssh_port> <user>@<host> "
  curl -sb /tmp/xui-cookie.txt \
    -X POST http://127.0.0.1:<panel_port><base_path>panel/api/inbounds/add \
    -H 'Content-Type: application/json' \
    -d '{
      \"remark\": \"vless-ws\",
      \"enable\": true,
      \"protocol\": \"vless\",
      \"listen\": \"127.0.0.1\",
      \"port\": 10000,
      \"settings\": \"{\\\"clients\\\":[{\\\"id\\\":\\\"'\"$UUID\"'\\\",\\\"flow\\\":\\\"\\\"}],\\\"decryption\\\":\\\"none\\\"}\",
      \"streamSettings\": \"{\\\"network\\\":\\\"ws\\\",\\\"wsSettings\\\":{\\\"path\\\":\\\"/ws/\\\"}}\",
      \"sniffing\": \"{\\\"enabled\\\":true,\\\"destOverride\\\":[\\\"http\\\",\\\"tls\\\"]}\"
    }'
"

3. Add Nginx location for the WS path

Append to the existing Nginx config (/etc/nginx/conf.d/x-ui.conf):

location /ws/ {
    proxy_pass http://127.0.0.1:10000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade websocket;
    proxy_set_header Connection upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_read_timeout 86400s;
}

Reload Nginx: systemctl reload nginx

4. Report client config

Output the connection info for the user to import into their client (v2rayN, Clash, etc.):

Protocol : VLESS
Address  : <domain>
Port     : 443
UUID     : <uuid>
Transport: WebSocket
Path     : /ws/
TLS      : TLS
SNI      : <domain>

Or generate a VLESS share link:

vless://<uuid>@<domain>:443?encryption=none&security=tls&type=ws&path=%2Fws%2F&sni=<domain>#vless-ws

Alternative: VLESS + Reality (no CDN, no domain needed)

Reality is the best option when no domain is available. It mimics a real TLS handshake against a target site.

# Generate Reality key pair inside the container/host
ssh -i <key_path> -p <ssh_port> <user>@<host> \
  "docker exec x-ui xray x25519"   # Docker
  # or: xray x25519                # native

Save the Private key and Public key output.

Create inbound via API (replace keys and port):

{
  "remark": "vless-reality",
  "enable": true,
  "protocol": "vless",
  "listen": "",
  "port": 443,
  "settings": "{\"clients\":[{\"id\":\"<uuid>\",\"flow\":\"xtls-rprx-vision\"}],\"decryption\":\"none\"}",
  "streamSettings": "{\"network\":\"tcp\",\"security\":\"reality\",\"realitySettings\":{\"show\":false,\"dest\":\"www.microsoft.com:443\",\"serverNames\":[\"www.microsoft.com\"],\"privateKey\":\"<private_key>\",\"shortIds\":[\"\"]}}"
}

Client connection info:

Protocol  : VLESS
Address   : <vps_ip>
Port      : 443
UUID      : <uuid>
Flow      : xtls-rprx-vision
Transport : TCP
Security  : Reality
PublicKey : <public_key>
SNI       : www.microsoft.com

Note: Reality listens on 0.0.0.0:443 (must be public). This is intentional — it's the proxy traffic port, not the panel.


Security Hardening for Inbounds

  • Always set "listen": "127.0.0.1" for WS/gRPC inbounds (Nginx handles public exposure).
  • Use a random UUID per client; rotate periodically.
  • Use a non-obvious WS path (e.g. /a3f9k2/ not /ws/).
  • Enable sniffing (destOverride: ["http","tls"]) to block DNS leaks.
  • For Reality, use a high-traffic legitimate domain as dest (e.g. www.microsoft.com, www.apple.com).